Как удалить повторяющиеся записи? - PullRequest
93 голосов
/ 17 ноября 2009

Я должен добавить уникальное ограничение к существующей таблице. Это хорошо, за исключением того, что в таблице уже есть миллионы строк, и многие строки нарушают уникальное ограничение, которое мне нужно добавить.

Какой самый быстрый подход к удалению поврежденных строк? У меня есть оператор SQL, который находит дубликаты и удаляет их, но для его запуска требуется вечность. Есть ли другой способ решить эту проблему? Может быть, резервное копирование таблицы, а затем восстановление после добавления ограничения?

Ответы [ 16 ]

3 голосов
/ 22 ноября 2009

Во-первых, вам нужно решить, какие из ваших «дубликатов» вы будете хранить. Если все столбцы равны, хорошо, вы можете удалить любой из них ... Но, возможно, вы хотите сохранить только самый последний или какой-то другой критерий?

Самый быстрый способ зависит от вашего ответа на вопрос выше, а также от процента дубликатов в таблице. Если вы отбрасываете 50% строк, вам лучше делать CREATE TABLE ... AS SELECT DISTINCT ... FROM ... ;, а если вы удаляете 1% строк, лучше использовать DELETE.

Также для операций обслуживания, подобных этой, обычно хорошо установить work_mem на хороший кусок вашей оперативной памяти: запустите EXPLAIN, проверьте число N сортировок / хэшей и установите для work_mem значение RAM / 2 / N. много оперативной памяти; это хорошо для скорости. Пока у вас есть только одно одновременное соединение ...

3 голосов
/ 17 ноября 2009
DELETE FROM table
  WHERE something NOT IN
    (SELECT     MAX(s.something)
      FROM      table As s
      GROUP BY  s.this_thing, s.that_thing);
1 голос
/ 18 февраля 2017
DELETE FROM tablename
WHERE id IN (SELECT id
    FROM (SELECT id,ROW_NUMBER() OVER (partition BY column1, column2, column3 ORDER BY id) AS rnum
                 FROM tablename) t
          WHERE t.rnum > 1);

Удалить дубликаты по столбцам и сохранить строку с самым низким идентификатором. Шаблон взят из postgres wiki

Используя CTE, вы можете добиться более читаемой версии вышеупомянутого через эту

WITH duplicate_ids as (
    SELECT id, rnum 
    FROM num_of_rows
    WHERE rnum > 1
),
num_of_rows as (
    SELECT id, 
        ROW_NUMBER() over (partition BY column1, 
                                        column2, 
                                        column3 ORDER BY id) AS rnum
        FROM tablename
)
DELETE FROM tablename 
WHERE id IN (SELECT id from duplicate_ids)
1 голос
/ 21 ноября 2013
CREATE TABLE test (col text);
INSERT INTO test VALUES
 ('1'),
 ('2'), ('2'),
 ('3'),
 ('4'), ('4'),
 ('5'),
 ('6'), ('6');
DELETE FROM test
 WHERE ctid in (
   SELECT t.ctid FROM (
     SELECT row_number() over (
               partition BY col
               ORDER BY col
               ) AS rnum,
            ctid FROM test
       ORDER BY col
     ) t
    WHERE t.rnum >1);
1 голос
/ 28 октября 2013

Это работает очень хорошо и очень быстро:

CREATE INDEX otherTable_idx ON otherTable( colName );
CREATE TABLE newTable AS select DISTINCT ON (colName) col1,colName,col2 FROM otherTable;
1 голос
/ 16 февраля 2010

Я работаю с PostgreSQL 8.4. Когда я запустил предложенный код, я обнаружил, что он не был фактически удаляя дубликаты. При запуске некоторых тестов я обнаружил, что добавление «DISTINCT ON (duplicate_column_name)» и «ORDER BY duplicate_column_name» сделали свое дело. Я не гуру SQL, я нашел это в PostgreSQL 8.4 SELECT ... DISTINCT doc.

CREATE OR REPLACE FUNCTION remove_duplicates(text, text) RETURNS void AS $$
DECLARE
  tablename ALIAS FOR $1;
  duplicate_column ALIAS FOR $2;
BEGIN
  EXECUTE 'CREATE TEMPORARY TABLE _DISTINCT_' || tablename || ' AS (SELECT DISTINCT ON (' || duplicate_column || ') * FROM ' || tablename || ' ORDER BY ' || duplicate_column || ' ASC);';
  EXECUTE 'DELETE FROM ' || tablename || ';';
  EXECUTE 'INSERT INTO ' || tablename || ' (SELECT * FROM _DISTINCT_' || tablename || ');';
  EXECUTE 'DROP TABLE _DISTINCT_' || tablename || ';';
  RETURN;
END;
$$ LANGUAGE plpgsql;
...