删除PostgreSQL中的重复记录

时间:2011-07-05 14:02:11

标签: sql postgresql duplicates

我在PostgreSQL 8.3.8数据库中有一个表,它没有键/约束,并且有多行具有完全相同的值。

我想删除所有重复项,并且每行只保留一份副本。

特别是有一列(名为“密钥”)可用于识别重复项(即每个不同的“密钥”应该只存在一个条目)。

我该怎么做? (理想情况下使用单个SQL命令) 在这种情况下速度不是问题(只有几行)。

13 个答案:

答案 0 :(得分:128)

更快的解决方案是

DELETE FROM dups a USING (
      SELECT MIN(ctid) as ctid, key
        FROM dups 
        GROUP BY key HAVING COUNT(*) > 1
      ) b
      WHERE a.key = b.key 
      AND a.ctid <> b.ctid

答案 1 :(得分:57)

DELETE FROM dupes a
WHERE a.ctid <> (SELECT min(b.ctid)
                 FROM   dupes b
                 WHERE  a.key = b.key);

答案 2 :(得分:37)

这是快速而简洁的:

DELETE FROM dupes T1
    USING   dupes T2
WHERE   T1.ctid < T2.ctid  -- delete the older versions
    AND T1.key  = T2.key;  -- add more columns if needed

另请参阅我在How to delete duplicate rows without unique identifier的回答,其中包含更多信息。

答案 3 :(得分:12)

我试过了:

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维基提供:

https://wiki.postgresql.org/wiki/Deleting_duplicates

答案 4 :(得分:6)

我必须创建自己的版本。 @a_horse_with_no_name编写的版本在我的桌子上太慢了(21M行)。 @rapimo根本不删除重复。

这是我在PostgreSQL 9.5上使用的

DELETE FROM your_table
WHERE ctid IN (
  SELECT unnest(array_remove(all_ctids, actid))
  FROM (
         SELECT
           min(b.ctid)     AS actid,
           array_agg(ctid) AS all_ctids
         FROM your_table b
         GROUP BY key1, key2, key3, key4
         HAVING count(*) > 1) c);

答案 5 :(得分:5)

我会使用临时表:

create table tab_temp as
select distinct f1, f2, f3, fn
  from tab;

然后,删除tab并将tab_temp重命名为tab

答案 6 :(得分:5)

EXISTS很简单,并且在大多数数据分发中速度最快:

DELETE FROM dupes d
WHERE  EXISTS (
   SELECT FROM dupes
   WHERE  key = d.key
   AND    ctid < d.ctid
   );

从每组重复的行(由相同的key定义)中,将一行保留为最少的ctid

结果与currently accepted answer by a_horse相同。只需 更快 ,因为EXISTS可以在找到第一个违规行后立即停止评估,而使用min()的替代项必须考虑所有行以计算最小值。速度与这个问题无关,但为什么不采用呢?

您可能希望在清理后添加一个UNIQUE constraint,以防止重复出现:

ALTER TABLE dupes ADD CONSTRAINT constraint_name_here UNIQUE (key);

关于系统列 ctid

如果表中的UNIQUE NOT NULL列中定义了其他任何列(如PRIMARY KEY),则一定要使用它而不是ctid

如果key可以是 NULL ,并且您也只希望其中之一,请使用IS NOT DISTINCT FROM而不是=。参见:

因为它比较慢,所以您可以按原样运行上面的查询,此外还可以运行

DELETE FROM dupes d
WHERE  key IS NULL
AND    EXISTS (
   SELECT FROM dupes
   WHERE  key IS NULL
   AND    ctid < d.ctid
   );

并考虑:

对于小型表,索引通常对性能没有帮助。而且我们不必再看了。

对于大表少量重复项,(key)上的现有索引可以提供很多帮助。

对于大部分重复,索引可能带来的成本多于收益,因为它必须同时保持最新状态。无论如何,查找没有索引的重复项会变得更快,因为有很多索引,EXISTS只需要查找一个即可。但是如果可以承受的话,请考虑使用一种完全不同的方法(即并发访问允许):将尚存的几行写入新表。这也消除了过程中的表(和索引)膨胀。参见:

答案 7 :(得分:1)

怎么样:

WITH
  u AS (SELECT DISTINCT * FROM your_table),
  x AS (DELETE FROM your_table)
INSERT INTO your_table SELECT * FROM u;

我一直在担心执行顺序,DELETE是否会在SELECT DISTINCT之前发生,但对我来说很好。 并且具有不需要任何有关表结构的知识的额外好处。

答案 8 :(得分:0)

这对我很有用。我有一个包含重复值的表,术语。执行查询以使用所有重复行填充临时表。然后我在临时表中运行了带有这些id的删除语句。 value是包含重复项的列。

        CREATE TEMP TABLE dupids AS
        select id from (
                    select value, id, row_number() 
over (partition by value order by value) 
    as rownum from terms
                  ) tmp
                  where rownum >= 2;

delete from [table] where id in (select id from dupids)

答案 9 :(得分:0)

另一种方法(仅当表中有id之类的唯一字段时才有效)按列查找所有唯一ID,并删除不在唯一列表中的其他ID

DELETE
FROM users
WHERE users.id NOT IN (SELECT DISTINCT ON (username, email) id FROM users);

答案 10 :(得分:0)

这是使用PARTITION BY的解决方案:

DELETE FROM dups
USING (
  SELECT
    ctid,
    (ctid != min(ctid) OVER (PARTITION BY key_column1, key_column2 [...])) AS is_duplicate
  FROM dups 
) dups_find_duplicates
WHERE dups.ctid == dups_find_duplicates.ctid
AND dups_find_duplicates.is_duplicate

答案 11 :(得分:0)

Postgresql 有 windows 函数,你可以使用 rank() 来归档你的目标,示例:

WITH ranked as (
    SELECT
        id, column1,
        "rank" () OVER (
            PARTITION BY column1
            order by column1 asc
        ) AS r
    FROM
        table1
) 
delete from table1 t1
using ranked
where t1.id = ranked.id and ranked.r > 1

答案 12 :(得分:-3)

适用于SQL的所有变体/风格(也适用于AWS REDSHIFT [POSTGRESQL])

1.删除重复项的最佳方法 - &gt;使用CTE

WITH DUPLICATE_CTE AS 
( SELECT KEY,COUNT(1) AS RANKED FROM <SCHEMANAME>.<TABLENAME>
  GROUP BY KEY )
DELETE FROM DUPLICATE_CTE WHERE RANKED > 1

2.Easy方法 - &gt;使用row_number()/ rank,dense_rank()函数

DELETE FROM <TABLE_ALIAS>
FROM (
SELECT <COLUMN_NAMES>,
ROW_NUMBER() OVER (PARTITION BY KEY) AS RANKED
FROM <SCHEMANAME>.<TABLENAME>
) <TABLE_ALIAS>
WHERE <TABLE_ALIAS>.RANKED >1

这可能比使用上述

更昂贵

3.Lay-Mans(又名LAME:p)方法(删除完全重复的最通用方法)

DROP TABLE IF EXISTS backupOfTheTableContainingDuplicates;

CREATE TABLE aNewEmptyTemporaryOrBackupTable 
AS SELECT DISTINCT * FROM originalTableContainingDuplicates;

TRUNCATE TABLE originalTableContainingDuplicates;

INSERT INTO originalTableContainingDuplicates SELECT * FROM 
aNewEmptyTemporaryOrBackupTable ;

DROP TABLE aNewEmptyTemporaryOrBackupTable ;

上述SQL脚本的解释

所以,

第一个查询确保,如果您有包含重复项的原始表的任何备份/临时表,则首先删除该表。

第二个查询,创建一个新表(临时/备份)表,原始表中包含重复的唯一条目,因此新临时表与原始表MINUS重复的条目相同。

第三个查询,截断或清空原始表。

第4个查询,将临时表中的所有唯一条目插入或复制到最近被截断的原始表中(因此没有数据)。执行此查询后,将使用临时表中的UNIQUE数据填充原始表。

第5个查询,删除/删除不必要的临时表。

所以最终的结果是,原始表只有UNIQUE ENTRIES而没有重复。