PostgreSQL에서 중복 레코드를 찾는 방법


189

현재 다음과 같은 중복 필드를 허용하는 "user_links"라는 PostgreSQL 데이터베이스 테이블이 있습니다.

year, user_id, sid, cid

고유 제한 그러나 지금 확인하기 위해 제약 조건을 추가 할 찾고, 현재 "ID"라는 첫 번째 필드 인 year, user_id, sidcid모든 고유하지만 중복 값은 이미이 제약 조건을 위반하는 존재하기 때문에 내가 제약 조건을 적용 할 수 없습니다.

모든 중복 항목을 찾는 방법이 있습니까?


답변:


335

기본 아이디어는 카운트 집계와 함께 중첩 쿼리를 사용하는 것입니다.

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에게 감사의 말을 전하고 싶습니다. 더 높은 가시성을 위해 이에 대한 답변을 제안합니다.
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

5
내가 본 것처럼이 쿼리는 그룹 내의 모든 행을 고려하지 않습니다. 그것은 중복의 일부 ROWNUM = 1 올바른 날 내가 잘못 만약에있을 것입니다, 뭔가 복제 만 보여줍니다
블라디미르 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

4
@VladimirFilipchenko 그냥 교체 ROW_NUMBER()COUNT(*)하고, 추가 rows between unbounded preceding and unbounded followingORDER BY id asc
alexkovelsky

2
내가 찾은 다른 솔루션보다 훨씬 낫습니다. 도에 속는 삭제 잘 동등하게 작동 DELETE ...USING하고 약간의 조정
브랜든

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에 대해 duplicate_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

더 쉽게하기 위해 열 연도에만 고유 제약 조건을 적용하고 기본 키는 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;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.