PostgreSQLで重複レコードを見つける方法


189

"user_links"と呼ばれるPostgreSQLデータベーステーブルがあり、現在次の重複フィールドを許可しています。

year, user_id, sid, cid

ユニーク制約は、しかし、私は今、確認するために制約を追加するために探しています、現在「ID」と呼ばれる最初のフィールドでyearuser_idsidおよびcidすべて一意であるが、重複した値がすでにこの制約に違反が存在するので、私は制約を適用することはできません。

すべての重複を見つける方法はありますか?


回答:


333

基本的な考え方は、カウント集計でネストされたクエリを使用することです。

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 2016年

これらのオプションは私にとっては機能し、他のオプションは結果をグループ化し、これらのオプションは重複したレコードだけでなくすべての重複したレコードを私に与えてくれました、ありがとう!
rome3ro 2017

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
これは速いです!ほんの一瞬で数百万行以上を処理しました。他の回答が
ハングアップし

5
私が見る限り、このクエリはグループ内のすべての行を考慮しません。何かへの重複のみが表示され、重複の一部はrownum = 1になります。間違っている場合は修正してください
Vladimir Filipchenko

9
:Filipchenkoは、すべての行でそれを持っているAlexkovelsky溶液にレベルを追加する@vladimirSELECT * 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;
ルドロイド

3
@VladimirFilipchenko単にで置き換えROW_NUMBER()、後にCOUNT(*)追加rows between unbounded preceding and unbounded followingORDER BY id asc
alexkovelsky 2017年

2
私が見つけた他のソリューションよりもはるかに優れています。DELETE ...USINGいくつかのマイナーな調整を伴うデュープの削除にも同様に機能します
Brandon

6

重複するフィールドで同じテーブルに結合してから、idフィールドで逆結合することができます。最初のテーブルエイリアス(tn1)からidフィールドを選択し、次に2番目のテーブルエイリアスの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;

明らかに、1つのidのduplicate_entries配列にあるidは、結果セットに独自のエントリも持っています。この結果セットを使用して、「真実」のソースになるIDを決定する必要があります。削除してはならない1つのレコード。多分あなたはこのようなことをすることができます:

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.