2009-07-24 6 views
6

У кого-нибудь есть изящная инструкция sql для удаления дубликатов записей из таблицы, но только в том случае, если имеется больше, чем x количество дубликатов? Таким образом, это позволяет до 2 или 3 дубликатов, но так оно и есть?SQL Query - Удалить дубликаты, если более трех дубликатов?

В настоящее время у меня есть оператор выбора, который делает следующее:

delete table 
from table t 
left outer join (
select max(id) as rowid, dupcol1, dupcol2 
from table 
group by dupcol1, dupcol2 
) as keeprows on t.id=keeprows.rowid 
where keeprows.rowid is null 

Это прекрасно работает. Но теперь то, что я хотел бы сделать, это удалить только эти строки, если у них больше двух дубликатов.

Благодаря

+0

при наличии 5 дубликатов, вы хотите только один остались после удаления, или три? – Stobor

ответ

7
with cte as (
    select row_number() over (partition by dupcol1, dupcol2 order by ID) as rn 
    from table) 
delete from cte 
    where rn > 2; -- or >3 etc 

Запрос производства 'номер строки' для каждой записи, сгруппированных по (dupcol1, dupcol2) и заказанный ID , Фактически этот номер строки подсчитывает «дубликаты», которые имеют одинаковые dupcol1 и dupcol2 и присваивают, а затем номер 1, 2, 3 .. N, порядок по ID. Если вы хотите сохранить только 2 «дубликаты», то вам необходимо удалить те, которые были присвоены номера 3,4,.. N и что является частью позаботятся в DELLETE.. WHERE rn > 2;

С помощью этого метода вы можете изменить ORDER BY в соответствии с вашими предпочтительным (например, ORDER BY ID DESC), так что LATEST имеет rn=1, а затем следующее последствие: rn = 2 и т. д. Остальное остается неизменным, DELETE удалит только самые старые из них, так как они имеют самые высокие номера строк.

В отличие от this closely related question, поскольку условие становится более сложным, использование CTE и row_number() упрощается. Производительность может быть проблематичной, если нет соответствующего индекса доступа.

+0

Спасибо Ремусу, но поскольку я не эксперт по sql, а не как знакомый с конкретными ключевыми словами 2005 года, не могли бы вы объяснить мне, что делает запрос? Я предполагаю, что раздел является хорошим ярлыком для левого присоединения к сгруппированной таблице, похожей на мой первый пример? Итак, ваша вторая строка возвращает новый идентификатор всех повторяющихся записей на основе предоставленных столбцов? Является ли число раз дублирование строки на основе столбцов во второй строке? Спасибо. – Scott

+1

Запрос производит «номер строки» для каждой записи, сгруппированный по (dupcol1, dupcol2) и упорядоченный по идентификатору. Фактически этот номер строки подсчитывает «дубликаты», которые имеют одинаковые dupcol1 и dupcol2 и присваивают, а затем номер 1, 2, 3 .. N, порядок по ID. Если вы хотите сохранить всего 2 дубликата, вам нужно удалить те, которым были присвоены номера 3,4, .. N, и это часть, которую заботит DELLETE .. WHERE rn> 2; HTH, дайте мне знать, если все еще неясно. –

+0

Нет, я получил, спасибо большое. Но последнее, что я хочу, чтобы я всегда сохранял запись LATEST. Итак, если я записываю записи с <2 дубликатами, а затем выкидываю всех остальных, как я могу изменить запрос, чтобы сохранить последние (max (id)) две или три записи таблицы. В качестве примера: скажем, что одна запись указана в нашей системе 10 раз. Это нарушает правило дублирования «2». Мы хотели бы удалить 7 дубликатов, оставив только одну главную запись и два дубликата. По основной записи мы имеем в виду самую последнюю (самую последнюю) запись, которая попала в систему. – Scott

3

HAVING ваш друг

select id, count(*) cnt from table group by id having cnt>2

0

Довольно поздно, но простейшее решение может быть следующим предположим, что мы имеем таблицу EMP_DEPT (EmpID, deptid), который имеет одинаковые строки, Здесь я использовал @count, как varibale .. например, 2 мы копируем позволило затем @count = 2 На базе Oracle

delete from emp_dept where @Count <= (select count(1) from emp_dept i where i.empid = emp_dept.empid and i.deptid = emp_dept.deptid and i.rowid < emp_dept.rowid) 

На SQL-сервере или anydatabase, который не поддерживает идентификатор строки своего рода особенность, нам нужно добавить столбец идентификаторов только для идентификации каждой строки. говорят, что мы добавили нидь как тождество к столу

alter table emp_dept add nid int identity(1,1) -- to add identity column 

Теперь запрос, чтобы удалить дубликаты можно записать в виде

delete from emp_dept where @@Count <= (select count(1) from emp_dept i where i.empid = emp_dept.empid and i.deptid = emp_dept.deptid and i.nid< emp_dept.nid) 

Здесь понятие удалить все строки, для которых существует другие строки, которые имеют аналогичные но n или большее число меньших rowid или identity. Следовательно, если существуют повторяющиеся строки, то тот, у которого есть более высокий идентификатор строки или идентификатор, будет удален. и для строки нет дубликатов, которые не могут найти более низкий идентификатор строки, поэтому не будут удалены.

0

Oracle:

delete from test where rowid = ANY (select min(test.rowid) from test left 
    outer join 
    (select min(rowid) row_id from test group by id,name)t on 
    test.rowid=t.row_id where t.row_id is null group by test.id,test.name); 
Смежные вопросы