2013-03-11 2 views
1

Использование SQL Server 2008. Я пытаюсь удалить несколько повторяющихся строк в таблице. Здесь перечислены таблицы и столбцы, которые имеют отношение:Удаление повторяющихся строк, отсутствующих в таблице ссылок

ItemTable 
---------- 
Id - autoincrement, PK 
ItemLabel - the actual identifier of the items 


Linktable 
---------- 
Id - autoincrement, PK 
ItemId - the Id from ItemTable 
RelatedItemId - the Id from RelatedItemTable 


RelatedItemTable 
------ 
no need to touch this with the query.. 

Так таблица ссылки не содержит фактический идентификатор элементов, но идущий номер строки из двух таблиц

Что нуждается Достигнуто: ItemTable содержит строки, которые имеют дубликат ItemLabel, где другая указана в таблице ссылок (с значением столбца Id), а другая нет. Из ItemTable следует удалить те, которые не связаны. Я знаю, как выбирать повторяющиеся строки, используя count и group by, но не смог выяснить, как удалить только те, которые не существуют в таблице ссылок. ItemTable также содержит дубликаты элементов без отношения, один из них должен оставаться (не имеет значения, какой).

http://www.sqlfiddle.com/#!3/9d181 Вот скрипта SQL с фиктивными данными.

P.S. Не спрашивайте, почему таблица ссылок использует текущий идентификатор вместо фактического идентификатора (который может быть PK'd) ... это устаревшая система.

ответ

0

Дайте этому попытку:

DELETE t 
OUTPUT deleted.* 
FROM ItemTable t 
JOIN (
SELECT DENSE_RANK() OVER (PARTITION BY ItemLabel ORDER BY lt.ItemID DESC, it.id) num 
     , it.Id 
FROM ItemTable it 
LEFT JOIN 
     LinkTable lt ON 
     lt.ItemId = it.id 
) t2 ON t2.Id = t.Id 
WHERE num > 1 

SQL Fiddle

Хотя описанный выше подход работает для вашей ситуации, я предлагаю подход, который является более читаемым и над которыми вы будете иметь больше контроля и лучшего обзора. Это многоэтапный подход, в котором каждый шаг может быть проанализированы и проверены:

-- get ItemLabels of duplicate records 
SELECT ItemLabel 
INTO #Duplicate_ItemLabels 
FROM ItemTable it 
GROUP BY 
     it.ItemLabel 
HAVING COUNT(*) > 1 

-- get ItemLabels of duplicate records that have at least one record related to LinkTable 
SELECT * 
INTO #Duplicate_ItemLabels_Related_To_LinkTable 
FROM #Duplicate_ItemLabels d1 
WHERE EXISTS 
(
     SELECT * 
     FROM ItemTable it 
     JOIN Linktable lt ON 
       lt.ItemID = it.ID 
     WHERE it.ItemLabel = d1.ItemLabel 
) 

-- get ItemLabels of duplicate records that don't have any records related to LinkTable 
SELECT ItemLabel 
INTO #Duplicate_ItemLabels_NOT_Related_To_LinkTable 
FROM #Duplicate_ItemLabels 
EXCEPT 
SELECT ItemLabel 
FROM #Duplicate_ItemLabels_Related_To_LinkTable 

-- delete unwanted records for ItemLabels that have records related to linkTable 
DELETE it 
OUTPUT deleted.* 
FROM ItemTable it 
JOIN #Duplicate_ItemLabels_Related_To_LinkTable dup ON 
     dup.ItemLabel = it.ItemLabel 
WHERE NOT EXISTS 
(
     SELECT * 
     FROM Linktable lt 
     WHERE lt.ItemID = it.ID 
) 

-- delete unwanted records for ItemLabels that don't have any records related to linkTable 
DELETE it 
OUTPUT deleted.* 
FROM ItemTable it 
JOIN #Duplicate_ItemLabels_NOT_Related_To_LinkTable dup ON 
     dup.ItemLabel = it.ItemLabel 
JOIN  
(
     -- records deleted will be all those that have ID greater than the smallest ID for this ItemLabel 
     SELECT ItemLabel 
       , MIN(ID) ID 
     FROM ItemTable dup 
     GROUP BY 
       dup.ItemLabel 
)  gr ON 
     gr.ID < it.ID 
AND  gr.ItemLabel = dup.ItemLabel 

-- if after these DELETEs there are still duplicate records, it 
-- means that there are records for same ItemLabel with 
-- different ID and all of them are related to LinkTable 

Вы можете легко изменить его, результаты тестирования и управления, какие записи будут удалены. Я создал SQL Fiddle, в котором я поместил разные образцы данных, чтобы вы могли видеть, как они обрабатываются.

Для выборки данных второго подхода, который я также добавил записей в ItemTable, где у вас есть такой же ItemLabel с различными ID более чем одна из которых связана с LinkTable (ни один из них не будет удален, произвольно).

+0

Первые два будут удалять все повторяющиеся строки AFAIK, проблема в том, что дубликат, который находится в таблице ссылок, должен оставаться неповрежденным. Третий запрос ничего не возвращал, когда я менял DELETE на SELECT * .. –

+0

@TeemuTerho Лучше всего было бы разместить некоторые выборочные данные вместе с ожидаемым выходом. Если вы это сделаете, я уверен, что многие люди здесь могут дать вам ответ :) –

+0

@TeemuTerho Я обновил свой ответ, я считаю, что он делает то, что ожидается, см. SQL Fiddle ... –

0

Соедините оба стола, используя LEFT JOIN. Очевидно, что несуществующий ItemTable.ID будет иметь нулевые значения на Linktable.ItemID, и это будет отфильтровано в вашем пункте WHERE.

DELETE a 
FROM ItemTable a 
     LEFT JOIN Linktable b 
      ON a.ID = b.ItemID 
WHERE b.ItemID IS NULL 
+0

Я попытался сделать выбор с этим запросом, похоже, что он также возвращал не дублированные строки. Можно удалить только те, которые дублируются, а не в таблице ссылок. Thx для форматирования edit btw :) –

+0

можете ли вы предоставить образцы записей по крайней мере 4 за таблицу ': D' с совпадением и отсутствием данных, чтобы мы могли играть с ней': D' –

+0

Скорее не сделаю этого, мне будет легче проконсультируйтесь с мастером IRL SQL позже в тот же день, если это не будет разрешено только с описанием. –

Смежные вопросы