Yes, the delete step is going to be a pain and might cause (slight) performance issues, but see here for a ray of hope in that regard.
The best solution (prevention is better than cure) is to create a UNIQUE index on your dna field (or whatever you call it) and then this problem cannot arise in the future and the steps below become a one-off operation. Then, when loading a new batch/run, simply use a staging table which allows duplicates and then only copy in the UNIQUE
DNA sequences and then TRUNCATE
the staging table.
CREATE UNIQUE INDEX sequence_idx ON sequence (dna);
You could also consider PARTITION
ing before the operation - only HASH
partitions make sense in this case. However, if you're have no reason to partition then it's pointless for this operation which will only be a one-off because of the UNIQUE constraint!
Note that you are not normalising, rather you are deduplifying (if that's a word?). Here's a post from a guy who really knows his way around PostgreSQL on this topic.
Simple outline of deduplifcation steps follows (see fiddle here):
CREATE TABLE
sequence
(
id SERIAL PRIMARY KEY,
dna VARCHAR(50) NOT NULL
);
Populate it:
INSERT INTO sequence (dna) VALUES ('agctagctagct');
INSERT INTO sequence (dna) VALUES ('agctagctagct');
INSERT INTO sequence (dna) VALUES ('agctagctagct');
INSERT INTO sequence (dna) VALUES ('tagctagctagc');
INSERT INTO sequence (dna) VALUES ('tagctagctagc');
INSERT INTO sequence (dna) VALUES ('tagctagctagc');
INSERT INTO sequence (dna) VALUES ('tagctagctagc');
INSERT INTO sequence (dna) VALUES ('ggcggcggcggc');
INSERT INTO sequence (dna) VALUES ('ggcggcggcggc');
You can find your dups using this:
SELECT s.dna, COUNT(dna)
FROM sequence s
GROUP BY dna
HAVING COUNT(dna) > 1
ORDER BY s.dna;
Then delete your dups as follows:
DELETE FROM sequence s1 USING sequence s2
WHERE
s1.id < s2.id AND
s1.dna = s2.dna;
Check:
SELECT s.dna, COUNT(dna)
FROM sequence s
GROUP BY dna
ORDER BY s.dna;
Result:
dna count
agctagctagct 1
ggcggcggcggc 1
tagctagctagc 1
DELETE
of 56M took 3 mins.