2015-01-26 15 views
80

ich eine PostgreSQL-Datenbank-Tabelle „user_links“ genannt habe, die derzeit folgende doppelte Felder erlaubt:Wie doppelte Datensätze in PostgreSQL finden

year, user_id, sid, cid 

Die eindeutige Einschränkung ist derzeit das erste Feld „id“ genannt, aber ich Ich suche nun nach einer Einschränkung, um sicherzustellen, dass die , und cid alle eindeutig sind, aber ich kann die Einschränkung nicht anwenden, da bereits doppelte Werte existieren, die diese Einschränkung verletzen.

Gibt es eine Möglichkeit, alle Duplikate zu finden?

+2

möglich Duplikat [Finden Sie doppelte Zeilen mit PostgreSQL] (http://stackoverflow.com/questions/14471179/find-duplicate-rows-with-postgresql) – drs

Antwort

125

Die Grundidee wird eine verschachtelte Abfrage mit Count-Aggregation werden:

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

Sie können einstellen, die where-Klausel in der inneren Abfrage um die Suche einzuschränken.


Es ist auch eine gute Lösung für den in den Kommentaren erwähnt, (aber nicht jeder liest sie):

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

Oder kürzer:

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

Sie könnten auch mit HAVING: 'co1, col2, count (*) aus tbl group von col1, col2 HAVING count (*)> 1' – alexkovelsky

+0

Dank @alexkovelsky die have-Anweisung war einfacher für mich zu modifizieren und lief schneller. Ich würde eine Antwort damit für höhere Sichtbarkeit vorschlagen. – Vesanto

+0

diese Optionen funktionierte für mich, die anderen gruppiert die Ergebnisse, und diese Optionen gab mir alle doppelten Datensätze anstelle nur der Datensatz dupliziert, danke! – rome3ro

3

Sie zu denselben beitreten Tabelle auf den Feldern, die dupliziert werden und dann Anti-Join auf dem ID-Feld. Wählen Sie das ID-Feld aus dem ersten Tabellenalias (tn1) und verwenden Sie dann die Funktion array_agg für das ID-Feld des zweiten Tabellenalias. Damit die Funktion array_agg ordnungsgemäß funktioniert, gruppieren Sie die Ergebnisse nach dem Feld tn1.id. Dies erzeugt eine Ergebnismenge, die die ID eines Datensatzes und ein Array aller IDs enthält, die zu den Join-Bedingungen passen.

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; 

Offensichtlich ids, die für eine ID in der duplicate_entries Array sein wird, wird auch ihre eigenen Einträge in der Ergebnismenge. Sie müssen diese Ergebnismenge verwenden, um zu entscheiden, welche ID zur Quelle der Wahrheit werden soll. Der einzige Datensatz, der nicht gelöscht werden sollte. Vielleicht könnten Sie etwas tun:

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) 

Auswahl der niedrigsten Nummer ID, die Duplikate (vorausgesetzt, die ID int PK erhöht wird) haben. Dies wären die IDs, die Sie behalten würden.

+0

Versuchen Sie, eine Erläuterung zu Ihrem Code hinzuzufügen. – ianaya89

+0

@ ianaya89 Eine Erklärung hinzugefügt. – pwnyexpress

43

Von "Find duplicate rows with PostgreSQL" ist hier intelligente Lösung:

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

Das ist schnell!Arbeitete über Millionen von Zeilen in Sekundenbruchteilen. Andere Antworten hängen dort einfach ... – dmvianna

+1

Wie ich sehe, berücksichtigt diese Abfrage nicht alle Zeilen innerhalb einer Gruppe. Es zeigt nur Duplikate zu etwas, ein Teil der Duplikate wird mit rownum = 1. Korrigieren Sie mich Wenn ich falsch liege –

+1

@vladimir Filipchenko Um es mit allen Zeilen zu haben, fügen Sie eine Ebene zu Alexkovelsky Lösung: 'SELECT * FROM ( SELECT * , Blei (Zeile 1) über() als nextrow FROM ( SELECT *, ROW_NUMBER() OVER (w) als Zeilen FROM tbl WINDOW w AS (PARTITION BY Sp1, Sp2 ORDER BY col3) ) x ) y WHERE Zeile> 1 ODER nextrow> 1; ' –

Verwandte Themen