web-dev-qa-db-ger.com

So finden Sie doppelte Datensätze in PostgreSQL

Ich habe eine PostgreSQL-Datenbanktabelle mit dem Namen "user_links", die derzeit die folgenden doppelten Felder zulässt:

year, user_id, sid, cid

Die eindeutige Einschränkung ist derzeit das erste Feld mit dem Namen "id". Jetzt möchte ich jedoch eine Einschränkung hinzufügen, um sicherzustellen, dass year, user_id, sid und cid sind alle eindeutig, aber ich kann die Einschränkung nicht anwenden, da bereits doppelte Werte vorhanden sind, die diese Einschränkung verletzen.

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

140
John

Die Grundidee wird darin bestehen, eine verschachtelte Abfrage mit Zähleraggregation zu verwenden:

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

Sie können die where-Klausel in der inneren Abfrage anpassen, um die Suche einzugrenzen.


Es gibt eine andere gute Lösung für das, was in den Kommentaren erwähnt wird (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
254
Marcin Zablocki

Von " Finden Sie doppelte Zeilen mit PostgreSQL " hier ist eine clevere 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
73
alexkovelsky

Sie können in den Feldern, die dupliziert werden sollen, derselben Tabelle beitreten und dann im Feld id die Verknüpfung aufheben. Wählen Sie das ID-Feld aus dem ersten Tabellenalias (tn1) aus 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. Dadurch wird eine Ergebnismenge erstellt, die die ID eines Datensatzes und ein Array aller IDs enthält, die den Join-Bedingungen entsprechen.

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 haben IDs, die sich für eine ID im Array duplicate_entries befinden, auch ihre eigenen Einträge in der Ergebnismenge. Sie müssen diese Ergebnismenge verwenden, um zu entscheiden, welche ID Sie zur Quelle der 'Wahrheit' machen möchten. Der eine Datensatz, der nicht gelöscht werden soll. Vielleicht könntest du so etwas machen:

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)

Wählt die IDs mit der niedrigsten Zahl mit Duplikaten aus (vorausgesetzt, die ID erhöht sich in PK). Dies wären die Ausweise, die Sie aufbewahren würden.

4
pwnyexpress