我在PostgreSQL 8.3.8数据库中有一个表,它没有键/约束,并且有多行具有完全相同的值。
我想删除所有重复项,并且每行只保留一份副本。
特别是有一列(名为“密钥”)可用于识别重复项(即每个不同的“密钥”应该只存在一个条目)。
我该怎么做? (理想情况下使用单个SQL命令) 在这种情况下速度不是问题(只有几行)。
答案 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维基提供:
答案 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而没有重复。