【问题标题】:Delete duplicate rows using Sub-query使用子查询删除重复行
【发布时间】:2016-09-19 04:40:55
【问题描述】:

我正在使用 SQL Server 2014 并利用 Microsoft 提供的 AdventureWorks2012 示例数据库。

我正在尝试使用下面的子查询删除重复的行(选项 #2):

/* 选项 #2:子查询 */

--SELECT * FROM
DELETE SQLPractice.[dbo].[CURRENCY]
WHERE EXISTS (SELECT * 
              FROM
                  (SELECT 
                       NAME,
                       ROW_NUMBER () OVER (PARTITION BY NAME ORDER BY NAME) AS Flag
                   FROM  
                       SQLPractice.[dbo].[CURRENCY]) AS T
              WHERE Flag > 1) 
GO

但它会从表中删除所有行。

但另一个选项 (CTE) 确实只删除了重复的行。

/*** Option #3: CTE ***/ 
;WITH RepFlag AS
(
    SELECT 
        NAME,
        ROW_NUMBER () OVER (PARTITION BY NAME ORDER BY NAME) AS Flag
    FROM 
        SQLPractice.[dbo].[CURRENCY]
)
--SELECT * FROM RepFlag
DELETE RepFlag
WHERE Flag > 1

SELECT * 
FROM SQLPractice.[dbo].[CURRENCY]

请使用以下代码创建您自己的测试表。

/*** REMOVING DUPLICATE ROWS OPTION ***/
-- Creating a table 
SELECT TOP 0 *
INTO [dbo].[CURRENCY]
FROM AdventureWorks2012.Sales.Currency
WHERE NAME LIKE  '%A';

-- inserting duplicate rows 
INSERT [dbo].[CURRENCY]
SELECT * FROM AdventureWorks2012.Sales.Currency
WHERE NAME LIKE  '%A';

/***** SELECTING COUNT OF DUPLICATED ROWS *****/ 

/*** Option #1: "GROUP BY" with "HAVING" ***/ 
SELECT 
    NAME, COUNT(*) AS Qty   
FROM 
    SQLPractice.[dbo].[CURRENCY]
GROUP BY 
    NAME
HAVING 
    COUNT(*) >1
GO

【问题讨论】:

    标签: sql sql-server duplicates subquery


    【解决方案1】:

    如果您想使用subquery 删除重复的名称,请使用以下方法。

    DELETE t
    FROM  (SELECT  NAME,ROW_NUMBER () OVER (PARTITION BY NAME ORDER BY NAME) AS Flag
                  FROM  SQLPractice.[dbo].[CURRENCY]
                ) t
    WHERE t.Flag > 1
    GO
    

    您也可以使用 common table expression (CTE) 实现此目的。

    ;WITH cte_1
    AS (SELECT  NAME,ROW_NUMBER () OVER (PARTITION BY NAME ORDER BY NAME) AS Flag
                  FROM  SQLPractice.[dbo].[CURRENCY]
                ) 
    DELETE FROM cte_1
    WHERE Flag > 1
    

    【讨论】:

      【解决方案2】:

      一种可能的方法:

      DELETE tt
      FROM [your table] tt
         INNER JOIN
      
          (SELECT NAME, MIN(PK) AS MIN_KEY)
          FROM [your table]
          GROUP BY Name
          HAVING COUNT(*) > 1) dup ON dup.name = tt.name and tt.PK <> dup.MIN_KEY
      

      【讨论】:

      • 感谢 Anton 的解决方案,但由于我的表没有主键,因此无法正常工作。基本上,您提出的建议类似于 Akshey 的解决方案。
      • 如果您没有PK,那么您可以使用游标或“WHILE循环+临时表”。因此,对于每个重复的名称,您执行“DELETE TOP(xxx)...”,其中 xxx 是“[当前名称的重复次数] - 1”。也可以使用 SET ROWCOUNT 代替 DELETE TOP
      • 或者,您可以将不同的行(仅限重复项)复制到临时表,删除所有重复项,然后重新插入临时表中的数据。
      【解决方案3】:

      选项#2 删除所有行,因为EXISTS 中的子查询将始终返回表中所有行的行。 EXISTS 内部的子查询与父查询之间一定存在某种关系。子查询必须根据表的每一行产生不同的结果。当表具有标识列时,使用子查询删除重复行的一种选择是:

      DELETE from SQLPractice.[dbo].[CURRENCY]
      where identityCol not in ( select min(identityCol) FROM SQLPractice.[dbo].[CURRENCY] GROUP BY NAME)
      

      【讨论】:

      • 是的,非常感谢。我想过这个问题。只是想知道如何在不更改表定义的情况下绕过它。
      • 你可以用cte
      • 我可以。我用了它。如上面的代码所示。只是想探索不同的选择。
      【解决方案4】:

      在您的示例案例中, Row_Number() 不会帮助您解决问题。 因为即使在 CurrencyCode 的主键(候选字段)中,重复行也是相同的

      由于您只是将同一行插入到目标表中,因此 ModifiedDate 字段也是相同的。

      对于示例案例,您可以应用delete duplicate rows where no primary key exists中描述的解决方案

      你可以测试一下,下面的DELETE命令会删除表中的所有行

      delete [dbo].[CURRENCY]
      from [dbo].[CURRENCY]
      inner join (
          select ROW_NUMBER() over (partition by CurrencyCode order by ModifiedDate) rn, CurrencyCode, ModifiedDate from [dbo].[CURRENCY]
      ) dublicates
          on dublicates.CurrencyCode = [dbo].[CURRENCY].CurrencyCode and
             dublicates.ModifiedDate = [dbo].[CURRENCY].ModifiedDate
      where dublicates.rn > 1
      

      例如从教程中,建议使用光标方法 您可以使用以下

      DECLARE @Count int
      DECLARE @CurrencyCode varchar(10)
      DECLARE @ModifiedDate datetime
      
      DECLARE dublicate_cursor CURSOR FAST_FORWARD FOR
      SELECT CurrencyCode, ModifiedDate, Count(*) - 1
      FROM CURRENCY
      GROUP BY CurrencyCode, ModifiedDate
      HAVING Count(*) > 1
      
      OPEN dublicate_cursor
      
      FETCH NEXT FROM dublicate_cursor INTO @CurrencyCode, @ModifiedDate, @Count
      
      WHILE @@FETCH_STATUS = 0
      BEGIN
      
      SET ROWCOUNT @Count
      DELETE FROM CURRENCY WHERE CurrencyCode = @CurrencyCode AND ModifiedDate = @ModifiedDate
      SET ROWCOUNT 0
      
      FETCH NEXT FROM dublicate_cursor INTO @CurrencyCode, @ModifiedDate, @Count
      END
      
      CLOSE dublicate_cursor
      DEALLOCATE dublicate_cursor
      

      【讨论】:

        【解决方案5】:

        With语句只删除重复行,因为它收集所有重复记录,然后执行删除操作。

        虽然在您的子查询中您没有指定要删除哪些记录的位置条件,但它应该写成如下:

        DELETE SQLPractice.[dbo].[CURRENCY]
        WHERE EXISTS  
        (
            SELECT * FROM 
            (
                SELECT 
                NAME,
                ID,
                ROW_NUMBER () OVER (PARTITION BY NAME ORDER BY NAME) AS Flag
                FROM SQLPractice.[dbo].[CURRENCY] 
            )   AS T
            WHERE Flag > 1 AND T.ID=[CURRENCY].ID
        ) 
        

        【讨论】:

        • 这不会同时删除出现多次的货币的所有行吗?
        • 它将删除重复的项目,因此您对每种货币都有一个记录
        • 例如如果 'US Dollar' 出现两次,则内部查询将返回这两行的行。所以他们两个都会被删除
        • 但 OP 已将条件 WHERE Flag > 1,因此它不删除第一个标志行意味着从重复行中删除除第一个之外的所有行
        • 但在我的情况下,两行的“标志”列的值都是 2。因此两者都将被删除。
        【解决方案6】:

        你可以通过这个查询尝试这个只是重复的记录将被删除我做了这个基于货币重复值它删除所有重复值

        delete from test where currency in(select currency from test group by currency having count(*) >1)

        【讨论】:

        • 谢谢,但这会删除所有行。所以,这行不通。
        猜你喜欢
        • 1970-01-01
        • 2020-02-17
        • 1970-01-01
        • 2017-09-25
        • 1970-01-01
        • 2014-07-09
        • 2021-11-07
        • 2012-01-21
        • 2021-09-24
        相关资源
        最近更新 更多