如何删除没有唯一标识符的重复行

moe*_*moe 41 sql database postgresql duplicates netezza

我的表中有重复的行,我想以最有效的方式删除重复项,因为表很大.经过一番研究,我得出了这个问题:

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;
Run Code Online (Sandbox Code Playgroud)

但它只适用于SQL,而不适用于Netezza.它似乎不喜欢DELETE后面的WITH条款?

Gor*_*off 52

如果您没有其他唯一标识符,则可以使用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
                 );
Run Code Online (Sandbox Code Playgroud)

在每个表中都有一个唯一的,自动递增的id是个好主意.这样做delete是一个重要原因.

  • ctid是一个隐藏的领域.检索表定义时,它不会显示.它是一种内部行号. (2认同)
  • `where not exists`将删除没有**重复的行**.应该是`where exists(选择1`` (2认同)

isa*_*pir 36

我喜欢@ 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;
Run Code Online (Sandbox Code Playgroud)

如果你想删除它们之前查看的记录,然后只需更换DELETESELECT *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;
Run Code Online (Sandbox Code Playgroud)

更新:我在这里测试了一些不同的解决方案以提高速度.如果你不期望有很多重复,那么这个解决方案比具有NOT IN (...)子句的那个更好,因为它们在子查询中生成了很多行.

如果您重写要使用的查询,IN (...)那么它的执行方式与此处提供的解决方案类似,但SQL代码变得简洁得多.

更新2:如果您NULL在其中一个键列中有值(您实际上不应该IMO),那么您可以COALESCE()在该列的条件中使用,例如

  AND COALESCE(T1.col_with_nulls, '[NULL]') = COALESCE(T2.col_with_nulls, '[NULL]')
Run Code Online (Sandbox Code Playgroud)

  • 正如我在回答开头所写:`我喜欢 @erwin-brandstetter 的解决方案,但想展示一个解决方案......`。在发现性能优势后,我更喜欢“USING”解决方案,尤其是对于大表。我添加了一个示例来展示如何处理 `NULL` 值。 (2认同)

Erw*_*ter 22

在一个完美的世界中,每个表都有某种唯一的标识符.
在不存在任何唯一的列(或其组合)的,使用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
Run Code Online (Sandbox Code Playgroud)

上述查询很简单,只列出一次列名.NOT IN (SELECT ...)当涉及NULL值时,是一种棘手的查询样式,但系统列ctid永远不会为NULL.看到:

使用@GordonEXISTS证明的通常更快.因此,像@isapir这样USING子句后来添加了一个自连接.两者都应该导致相同的查询计划.

但请注意一个重要区别:这些其他查询将NULL值视为不相等,而GROUP BY(DISTINCTDISTINCT ON ())将NULL值视为相等.如果定义了键列,则无关紧要NOT NULL.否则,根据您对"重复"的定义,您将需要一种或另一种方法.或者用于IS NOT DISTINCT FROM比较值(可能无法使用某些索引).

免责声明:

ctid是Postgres的内部实现细节,它不在SQL标准中,可以在没有警告的情况下在主要版本之间进行更改(即使这不太可能).由于后台进程或并发写操作(但不在同一命令中),其值可以在命令之间更改.

有关:

在旁边:

DELETE语句的目标不能是CTE,只能是基础表.这是SQL Server的溢出效应 - 就像你的整个方法一样.


Bru*_*lza 10

这是我想出来的,使用一个 group by

DELETE FROM mytable
WHERE id NOT in (
  SELECT MIN(id) 
  FROM mytable
  GROUP BY name, address, zipcode
)
Run Code Online (Sandbox Code Playgroud)

它删除重复项,保留最旧的重复记录.


Viv*_* S. 6

我们可以使用窗口函数来非常有效地删除重复行:

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);
Run Code Online (Sandbox Code Playgroud)

一些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));
Run Code Online (Sandbox Code Playgroud)