Как найти повторяющиеся записи в PostgreSQL

70

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

year, user_id, sid, cid

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

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

Теги:
duplicates

3 ответа

118
Лучший ответ

Основная идея будет заключаться в использовании вложенного запроса с агрегацией 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
  • 44
    Вы также можете использовать HAVING: select co1, col2, count(*) from tbl group by col1, col2 HAVING count(*)>1
  • 0
    Спасибо @alexkovelsky за то, что мне легче было изменить заявление, и он работал быстрее. Я бы предложил ответ с ним для большей наглядности.
Показать ещё 2 комментария
38

От "Найти повторяющиеся строки с 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
  • 8
    Это быстро! Работал над миллионами строк за доли секунды. Другие ответы просто повесили там ...
  • 4
    Как я вижу, этот запрос не учитывает все строки в группе. Он показывает только дубликаты чего-либо, часть дубликатов будет с rownum = 1. Поправьте меня, если я ошибаюсь
Показать ещё 4 комментария
2

Вы можете присоединиться к той же таблице в полях, которые будут дублироваться, а затем анти-присоединиться к полю 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). Это будет идентификатор, который вы сохранили бы.

  • 0
    Попробуйте добавить объяснение того, что делает ваш код.
  • 0
    @ ianaya89 Добавлено объяснение.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню