如何在PostgreSQL中查找重复记录


189

我有一个名为“ user_links”的PostgreSQL数据库表,该表当前允许以下重复字段:

year, user_id, sid, cid

唯一的限制是目前所谓的“ID”的第一场,但是我现在希望增加一个约束,以确保yearuser_idsidcid都是独一无二的,但由于重复的值已存在违反此约束我不能应用约束。

有没有办法找到所有重复项?


Answers:


334

基本思想是将嵌套查询与计数聚合一起使用:

select * from yourTable ou
where (select count(*) from yourTable inr
where inr.sid = ou.sid) > 1

您可以调整内部查询中的where子句以缩小搜索范围。


对于注释中提到的问题,还有一个很好的解决方案(但不是每个人都阅读它们):

select Column1, Column2, count(*)
from yourTable
group by Column1, Column2
HAVING count(*) > 1

或更短:

SELECT (yourTable.*)::text, count(*)
FROM yourTable
GROUP BY yourTable.*
HAVING count(*) > 1

65
您还可以使用HAVING:select co1, col2, count(*) from tbl group by col1, col2 HAVING count(*)>1
alexkovelsky

1
谢谢@ alexkovelsky,having语句对我来说更容易修改,运行起来也更快。我建议用它来获得更高的知名度。
Vesanto

这些选项对我有用,其他选项将结果分组,这些选项给了我所有重复的记录,而不仅仅是重复的记录,谢谢!
rome3ro

1
我对你的回答有点慢。在10k行* 18列的表上,查询耗时8秒
aydow

1
多数民众赞成在果酱在那里。哎呀。谢谢。💯–
dps

90

从“ 使用PostgreSQL查找重复的行 ”中,这是一个聪明的解决方案:

select * from (
  SELECT id,
  ROW_NUMBER() OVER(PARTITION BY column1, column2 ORDER BY id asc) AS Row
  FROM tbl
) dups
where 
dups.Row > 1

11
快!在几分之一秒内处理了数百万行。其他答案就挂在那里……
dmvianna '16

5
据我所知,此查询未考虑组中的所有行。它仅显示某些内容的重复项,部分重复项将具有rownum =1。纠正我如果我错了
Vladimir Filipchenko

9
@vladimir Filipchenko要使用所有行,请为Alexkovelsky解决方案添加一个层次:SELECT * FROM ( SELECT *, LEAD(row,1) OVER () AS nextrow FROM ( SELECT *, ROW_NUMBER() OVER(w) AS row FROM tbl WINDOW w AS (PARTITION BY col1, col2 ORDER BY col3) ) x ) y WHERE row > 1 OR nextrow > 1;
Le Droid

3
@VladimirFilipchenko只需更换ROW_NUMBER()COUNT(*),并添加rows between unbounded preceding and unbounded followingORDER BY id asc
alexkovelsky

2
比我发现的其他解决方案好得多。在删除重复项以及进行DELETE ...USING一些小的调整方面也同样有效
Brandon

6

您可以在将要重复的字段上联接到同一表,然后在id字段上进行反联接。从第一个表别名(tn1)中选择id字段,然后在第二个表别名的id字段上使用array_agg函数。最后,为了使array_agg函数正常工作,您将根据tn1.id字段对结果进行分组。这将产生一个结果集,其中包含记录的ID和适合联接条件的所有ID的数组。

select tn1.id,
       array_agg(tn2.id) as duplicate_entries, 
from table_name tn1 join table_name tn2 on 
    tn1.year = tn2.year 
    and tn1.sid = tn2.sid 
    and tn1.user_id = tn2.user_id 
    and tn1.cid = tn2.cid
    and tn1.id <> tn2.id
group by tn1.id;

显然,将在一个ID的plicate_entries数组中的ID在结果集中也将具有自己的条目。您将必须使用此结果集来确定要成为“真相”来源的ID。一条不应删除的记录。也许您可以执行以下操作:

with dupe_set as (
select tn1.id,
       array_agg(tn2.id) as duplicate_entries, 
from table_name tn1 join table_name tn2 on 
    tn1.year = tn2.year 
    and tn1.sid = tn2.sid 
    and tn1.user_id = tn2.user_id 
    and tn1.cid = tn2.cid
    and tn1.id <> tn2.id
group by tn1.id
order by tn1.id asc)
select ds.id from dupe_set ds where not exists 
 (select de from unnest(ds.duplicate_entries) as de where de < ds.id)

选择具有重复项的最低编号的ID(假设ID递增int PK)。这些就是您将保留的ID。


3

为了简化起见,我假设您希望仅对列year应用唯一约束,并且主键是名为id的列。

为了找到重复的值,您应该运行,

SELECT year, COUNT(id)
FROM YOUR_TABLE
GROUP BY year
HAVING COUNT(id) > 1
ORDER BY COUNT(id);

使用上面的sql语句,您将获得一个包含表中所有重复年份的表。为了删除除最新重复项以外的所有重复项,应使用上述sql语句。

DELETE
FROM YOUR_TABLE A USING YOUR_TABLE_AGAIN B
WHERE A.year=B.year AND A.id<B.id;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.