Подтвердить что ты не робот

Как удалить повторяющиеся строки без уникального идентификатора

У меня есть повторяющиеся строки в моей таблице, и я хочу удалить дубликаты наиболее эффективным способом, так как таблица большая. После некоторых исследований у меня появился этот запрос:

WITH TempEmp AS
(
SELECT name, ROW_NUMBER() OVER(PARTITION by name, address, zipcode ORDER BY name) AS duplicateRecCount
FROM mytable
)
-- Now Delete Duplicate Records
DELETE FROM TempEmp
WHERE duplicateRecCount > 1;

Но он работает только в SQL, а не в Netezza. Казалось бы, это не нравится DELETE после предложения WITH?

4b9b3361

Ответ 1

Мне нравится решение @erwin-brandstetter, но я хотел показать решение с ключевым словом USING:

DELETE   FROM table_with_dups T1
  USING       table_with_dups T2
WHERE  T1.ctid    < T2.ctid       -- delete the "older" ones
  AND  T1.name    = T2.name       -- list columns that define duplicates
  AND  T1.address = T2.address
  AND  T1.zipcode = T2.zipcode;

Если вы хотите просмотреть записи перед их удалением, просто замените DELETE на SELECT * и USING запятой ,, т.е.

SELECT * FROM table_with_dups T1
  ,           table_with_dups T2
WHERE  T1.ctid    < T2.ctid       -- select the "older" ones
  AND  T1.name    = T2.name       -- list columns that define duplicates
  AND  T1.address = T2.address
  AND  T1.zipcode = T2.zipcode;

Обновление: здесь я проверил несколько различных решений на скорость. Если вы не ожидаете много дубликатов, то это решение работает намного лучше, чем те, у которых есть предложение NOT IN (...), поскольку они генерируют много строк в подзапросе.

Если переписать запрос для использования IN (...), он будет работать аналогично решению, представленному здесь, но код SQL станет гораздо менее лаконичным.

Обновление 2: если у вас есть значения NULL в одном из ключевых столбцов (что вам действительно не следует использовать в IMO), тогда вы можете использовать COALESCE() в условии для этого столбца, например

  AND COALESCE(T1.col_with_nulls, '[NULL]') = COALESCE(T2.col_with_nulls, '[NULL]')

Ответ 2

Если у вас нет другого уникального идентификатора, вы можете использовать ctid:

delete from mytable
    where exists (select 1
                  from mytable t2
                  where t2.name = mytable.name and
                        t2.address = mytable.address and
                        t2.zip = mytable.zip and
                        t2.ctid > mytable.ctid
                 );

Это хорошая идея иметь уникальный, автоматически увеличивающий id в каждой таблице. Выполнение delete, как это, является одной из важных причин.

Ответ 3

В идеальном мире каждая таблица имеет уникальный идентификатор.
При отсутствии какого-либо уникального столбца (или их комбинации) используйте столбец ctid:

DELETE FROM tbl
WHERE  ctid NOT IN (
   SELECT min(ctid)                    -- ctid is NOT NULL by definition
   FROM   tbl
   GROUP  BY name, address, zipcode);  -- list columns defining duplicates

Вышеприведенный запрос является коротким, удобно перечисляя имена столбцов только один раз. NOT IN (SELECT ...) - сложный стиль запроса, когда могут использоваться значения NULL, но системный столбец ctid никогда не имеет значения NULL. См:

Использование EXISTS как продемонстрировано @Gordon обычно быстрее. Так же как и само присоединение к предложению USING , как @isapir, добавленный позже. Оба должны привести к одному и тому же плану запросов.

Но обратите внимание на важное отличие: эти другие запросы обрабатывают значения NULL как , не равные, тогда как GROUP BY (или DISTINCT или DISTINCT ON ()) обрабатывает значения NULL как равные. Не имеет значения, определены ли ключевые столбцы NOT NULL. Иначе, в зависимости от вашего определения "дубликат", вам понадобится тот или иной подход. Или используйте IS NOT DISTINCT FROM для сравнения значений (которые могут быть не в состоянии использовать некоторые индексы).

Отказ от ответственности:

ctid является внутренней деталью реализации Postgres, она не входит в стандарт SQL и может быть изменена между основными версиями без предупреждения (даже если это очень маловероятно). Его значения могут меняться между командами из-за фоновых процессов или одновременных операций записи (но не внутри одной команды).

Связанный:

Помимо:

Целью оператора DELETE не может быть CTE, только базовая таблица. Это побочный эффект от SQL Server - как и весь ваш подход.

Ответ 4

Вот что я придумал, используя group by

DELETE FROM mytable
WHERE id NOT in (
  SELECT MIN(id) 
  FROM mytable
  GROUP BY name, address, zipcode
)

Он удаляет дубликаты, сохраняя самую старую запись с дубликатами.

Ответ 5

Мы можем использовать оконную функцию для очень эффективного удаления повторяющихся строк:

DELETE FROM tab 
  WHERE id IN (SELECT id 
                  FROM (SELECT row_number() OVER (PARTITION BY column_with_duplicate_values), id 
                           FROM tab) x 
                 WHERE x.row_number > 1);

Некоторая оптимизированная версия PostgreSQL (с ctid):

DELETE FROM tab 
  WHERE ctid = ANY(ARRAY(SELECT ctid 
                  FROM (SELECT row_number() OVER (PARTITION BY column_with_duplicate_values), ctid 
                           FROM tab) x 
                 WHERE x.row_number > 1));

Ответ 6

Действительный синтаксис указан в http://www.postgresql.org/docs/current/static/sql-delete.html

Я бы изменил вашу таблицу, чтобы добавить уникальный автоматически увеличивающийся идентификатор первичного ключа, чтобы вы могли выполнить запрос, подобный следующему, который будет сохранять первый из каждого набора дубликатов (то есть тот, который имеет самый низкий идентификатор). Обратите внимание, что добавление ключа в Postgres немного сложнее, чем в некоторых других БД.

DELETE FROM mytable d USING (
  SELECT min(id), name, address, zip 
  FROM mytable 
  GROUP BY name, address, zip HAVING COUNT() > 1
) AS k 
WHERE d.id <> k.id 
AND d.name=k.name 
AND d.address=k.address 
AND d.zip=k.zip;

Ответ 7

Если вы хотите сохранить одну строку из повторяющихся строк в таблице.

create table some_name_for_new_table as 
(select * from (select *,row_number() over (partition by pk_id) row_n from 
your_table_name_where_duplicates_are_present) a where row_n = 1);

Это создаст таблицу, которую вы можете скопировать.

Перед копированием таблицы удалите столбец "row_n"

Ответ 8

Из документации удалить повторяющиеся строки

Частым вопросом в IRC является удаление строк, которые дублируются по набору столбцов, сохраняя только один с самым низким идентификатором. Этот запрос делает это для всех строк tablename, имеющих одинаковые столбцы1, column2 и column3.

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);

Иногда вместо поля идентификатора используется поле метки времени.

Ответ 9

Если вам нужен уникальный идентификатор для каждой строки, вы можете просто добавить один (последовательный или направляющий) и обработать его как суррогатный ключ.


CREATE TABLE thenames
        ( name text not null
        , address text not null
        , zipcode text not null
        );
INSERT INTO thenames(name,address,zipcode) VALUES
('James', 'main street', '123' )
,('James', 'main street', '123' )
,('James', 'void street', '456')
,('Alice', 'union square' , '123')
        ;

SELECT*FROM thenames;

        -- add a surrogate key
ALTER TABLE thenames
        ADD COLUMN seq serial NOT NULL PRIMARY KEY
        ;
SELECT*FROM thenames;

DELETE FROM thenames del
WHERE EXISTS(
        SELECT*FROM thenames x
        WHERE x.name=del.name
        AND x.address=del.address
        AND x.zipcode=del.zipcode
        AND x.seq < del.seq
        );

        -- add the unique constrain,so that new dupplicates cannot be created in the future
ALTER TABLE thenames
        ADD UNIQUE (name,address,zipcode)
        ;

SELECT*FROM thenames;