2013-03-11 13 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.. 

所以鏈接表不包含在項目的實際ID,但正在運行的行號要實現:ItemTable包含具有重複ItemLabel的行,其中另一個在鏈接表中列出(使用Id列的值),另一個不是。從ItemTable中,不鏈接的鏈接必須被刪除。我知道如何使用count和group by來選擇重複行,但一直未能弄清楚如何只刪除鏈接表中不存在的行。 ItemTable還包含沒有關係的項目的重複項目,其中一個項目必須保留(無關緊要)。

http://www.sqlfiddle.com/#!3/9d181這是一個SQL小提琴與虛擬數據。

P.S.不要問爲什麼鏈接表使用正在運行的ID而不是實際的ID(可能是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,我在其中放入了不同的數據樣本,以便了解它是如何處理的。

到你有相同ItemLabel不同ID其中超過一個關係到LinkTable(他們沒有被隨意刪除)品嚐第二種方法我還添加記錄ItemTable的數據。

+0

前兩個將刪除所有重複行AFAIK,這裏的問題是鏈接表中的副本必須保持不變。當我將DELETE更改爲SELECT *時,第三個查詢沒有任何返回。 – 2013-03-11 09:16:16

+0

@TeemuTerho最好的辦法是發佈一些示例數據以及預期的輸出。如果你這樣做,我敢肯定很多人在這裏可以給你答案:) – 2013-03-11 09:41:43

+0

@TeemuTerho我更新了我的答案,我相信它做到了什麼,看到SQL小提琴... – 2013-03-11 09:53:50

0

加入這兩個表使用LEFT JOIN。顯然不存在ItemTable.ID將在Linktable.ItemID上有null值,這將在您的WHERE子句中進行過濾。

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

我試着用這個查詢做一個選擇,它好像也返回了非重複的行。只有那些重複的和不在鏈接表中的可以被刪除。 Thx的格式編輯btw :) – 2013-03-11 08:50:41

+0

你可以給樣品記錄至少4每個表':D'與匹配和不匹配的數據,所以我們可以玩它:'D' – 2013-03-11 08:52:32

+0

寧願不這樣做,會更容易我如果僅僅通過描述得不到解決,請在當天晚些時候諮詢IRL SQL嚮導。 – 2013-03-11 08:56:17