Как найти дубликаты записей в PostgreSQL

У меня есть таблица базы данных PostgreSQL, называемая "user_links", которая в настоящее время допускает следующие повторяющиеся поля:

year, user_id, sid, cid

Единственное ограничение в настоящее время является первым полем с именем "id", однако теперь я хочу добавить ограничение, чтобы убедиться, что year, user_id, sid и cid уникальны, но я не могу примените ограничение, поскольку уже существуют повторяющиеся значения, которые нарушают это ограничение.

Есть ли способ найти все дубликаты?

Ответ 1

Основная идея будет заключаться в использовании вложенного запроса с агрегацией count:

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

Ответ 3

Вы можете присоединиться к той же таблице в полях, которые будут дублироваться, а затем анти-присоединиться к полю id. Выберите поле id из первого псевдонима таблицы (tn1), а затем используйте функцию array_agg в поле id второго псевдонима таблицы. Наконец, для правильной работы функции array_agg вы будете группировать результаты в поле tn1.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, также будет иметь свои собственные записи в наборе результатов. Вам придется использовать этот результирующий набор, чтобы решить, какой идентификатор вы хотите стать источником "истины". Одна запись, которую не следует удалять. Возможно, вы могли бы сделать что-то вроде этого:

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)

Выбирает идентификатор наименьшего номера, у которого есть дубликаты (при условии, что идентификатор увеличивает int PK). Это будет идентификатор, который вы сохранили бы.