And*_*jão 94 sql postgresql duplicates
我在PostgreSQL 8.3.8数据库中有一个表,它没有键/约束,并且有多行具有完全相同的值.
我想删除所有重复项,每行只保留1份.
特别是有一列(称为"密钥")可用于识别重复(即每个不同的"密钥"应该只存在一个条目).
我怎样才能做到这一点?(理想情况下使用单个SQL命令)在这种情况下,速度不是问题(只有几行).
rap*_*imo 151
更快的解决方案是
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
Run Code Online (Sandbox Code Playgroud)
a_h*_*ame 69
DELETE FROM dupes a
WHERE a.ctid <> (SELECT min(b.ctid)
FROM dupes b
WHERE a.key = b.key);
Run Code Online (Sandbox Code Playgroud)
isa*_*pir 55
这快速而简洁:
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
Run Code Online (Sandbox Code Playgroud)
另请参阅我的答案如何删除没有包含更多信息的唯一标识符的重复行.
Rad*_*iel 14
我试过这个:
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);
Run Code Online (Sandbox Code Playgroud)
由Postgres维基提供:
https://wiki.postgresql.org/wiki/Deleting_duplicates
Erw*_*ter 12
EXISTS
对于大多数数据分布来说,它很简单并且是最快的:
DELETE FROM dupes d
WHERE EXISTS (
SELECT FROM dupes
WHERE key = d.key
AND ctid < d.ctid
);
Run Code Online (Sandbox Code Playgroud)
从每组重复行(由相同的定义key
)中,这将保留具有最小值的一行ctid
。
结果与a_horse 当前接受的答案相同。只是更快,因为EXISTS
一旦找到第一个违规行就可以停止评估,而替代方案min()
必须考虑每组的所有行来计算最小值。这个问题与速度无关,但为什么不考虑呢?
您可能希望在清理后添加UNIQUE
约束,以防止重复项重新出现:
ALTER TABLE dupes ADD CONSTRAINT constraint_name_here UNIQUE (key);
Run Code Online (Sandbox Code Playgroud)
关于系统栏ctid
:
如果表中有任何其他列定义的UNIQUE NOT NULL
列(如 a PRIMARY KEY
),那么无论如何,请使用它而不是ctid
.
如果key
可以,NULL
而您也只想要其中之一,请使用IS NOT DISTINCT FROM
代替=
。看:
由于速度较慢,您可以改为按原样运行上述查询,另外:
ALTER TABLE dupes ADD CONSTRAINT constraint_name_here UNIQUE (key);
Run Code Online (Sandbox Code Playgroud)
并考虑:
对于小表,索引通常对性能没有帮助。我们不需要再看下去了。
对于大表和少量重复,现有的索引(key)
可以提供帮助(很多)。
对于大多数重复项,索引可能会增加成本而不是收益,因为它必须同时保持最新。无论如何,在没有索引的情况下查找重复项会变得更快,因为数量太多,EXISTS
只需要找到一个即可。但是,如果您负担得起,请考虑一种完全不同的方法(即并发访问允许):将少数幸存的行写入新表。这也消除了过程中的表(和索引)膨胀。看:
我不得不创建自己的版本.@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);
Run Code Online (Sandbox Code Playgroud)
我将使用一个临时表:
create table tab_temp as
select distinct f1, f2, f3, fn
from tab;
Run Code Online (Sandbox Code Playgroud)
然后,删除tab
并重命名tab_temp
为tab
。
另一种方法(仅当您有像id
表中这样的任何唯一字段时才有效)按列查找所有唯一 ID 并删除不在唯一列表中的其他 ID
DELETE
FROM users
WHERE users.id NOT IN (SELECT DISTINCT ON (username, email) id FROM users);
Run Code Online (Sandbox Code Playgroud)