【问题标题】:Removing duplicate rows that are not in a link table删除不在链接表中的重复行
【发布时间】:2013-03-11 08:40:04
【问题描述】:

使用 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 小提琴。

附:不要问为什么链接表使用正在运行的 id 而不是实际的 id(可能是 PK 的)......这是一个遗留系统。

【问题讨论】:

    标签: sql sql-server sql-delete


    【解决方案1】:

    试试这个:

    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 相关(它们都不会被任意删除)。

    【讨论】:

    • 前两个将删除所有重复的行AFAIK,这里的问题是链接表中的重复项必须保持不变。当我将 DELETE 更改为 SELECT * .. 时,第三个查询没有返回任何内容
    • @TeemuTerho 最好的办法是发布一些示例数据以及预期的输出。如果你这样做,我相信这里有很多人可以给你答案:)
    • @TeemuTerho 我更新了我的答案,我相信它符合预期,请参阅 SQL Fiddle...
    • 我将对其进行测试,我正在研究 JW 提供的解决方案(非常接近)。我也会试试你的,因为这是一种不同的方法。我还应该更新描述,因为它没有所有信息。忙着忙着呢:)
    • @TeemuTerho 我添加了新的查询和新的 SQL Fiddle,试试看 :)
    【解决方案2】:

    使用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
    

    【讨论】:

    • 我尝试使用此查询进行选择,似乎它也返回了非重复行。只有那些重复的且不在链接表中的才能被删除。谢谢格式化编辑顺便说一句:)
    • 你能给每张表至少 4 个样本记录:D 提供匹配和不匹配的数据,以便我们可以使用它:D
    • 宁愿不这样做,如果仅通过描述不能解决这个问题,我会更容易在当天晚些时候咨询 IRL SQL 向导。
    • sqlfiddle.com/#!3/7cb13/1 我对其进行了改造以反映我正在处理的数据。
    • 也就是说,只剩下1,5,6?我想你的意思是1,3,4,5
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-23
    相关资源
    最近更新 更多