T-SQL:删除所有重复的行但保留一行

nui*_*it9 254 sql t-sql sql-server

可能重复:
SQL - 如何删除重复的行?

我有一个行数非常多的表.不允许重复,但由于行如何创建的问题我知道这个表中有一些重复.我需要从关键列的角度消除额外的行.其他一些列的数据可能略有不同,但我并不关心.我仍然需要保留其中一行.SELECT DISTINCT将无法工作,因为它在所有列上运行,我需要根据键列抑制重复.

如何删除额外的行但仍保持有效?

Ben*_*hul 501

您没有说明您使用的是哪个版本,但在SQL 2005及更高版本中,您可以使用带有OVER子句的公用表表达式.它有点像这样:

WITH cte AS (
  SELECT[foo], [bar], 
     row_number() OVER(PARTITION BY foo, bar ORDER BY baz) AS [rn]
  FROM TABLE
)
DELETE cte WHERE [rn] > 1
Run Code Online (Sandbox Code Playgroud)

玩弄它,看看你得到了什么.

(编辑:为了有所帮助,有人编辑了ORDER BYCTE中的条款.要清楚,你可以按照你想要的任何顺序排序,它不一定是cte返回的列之一.事实上,一个常见的在这里使用情况是"富,酒吧"是组标识符和"巴兹"是某种形式的时间戳.为了保持最新,你会怎么做ORDER BY baz desc)

  • 刚刚回到这个答案并注意到问题:它将保留哪个骗局.如上所述,它将保留"第一"重复行,其中"第一"表示"根据baz的最低排序".当然,如果您不确定要删除/保留的内容,请将删除变为选择并确保.比抱歉更安全. (16认同)
  • 这将保留最后一个重复行或第一行? (5认同)
  • 如果行数较多,则可能不建议使用DELETE(完全恢复也会导致事务日志填满).最好做一个SELECT*INTO NewTable FROM cte,然后删除旧表.对于非常大的表,这将更快. (4认同)
  • 如果分批执行,则不要忘记在WITH之前的分号。交易https://msdn.microsoft.com/en-us/library/ms175972.aspx (2认同)
  • @SumGuy:不;行号就足够了。但我想先将此作为选择运行,以检查将要受影响的内容。另外,我只是进行了一次快速测试,看来SQL Server足够聪明,不会继续传送不需要的列。我通过查看实际执行计划中的输出列列表来确定这一点,在这种情况下,我选择了所有内容以及行号和仅行号;两者是相同的。 (2认同)

jam*_*ams 106

示例查询:

DELETE FROM Table
WHERE ID NOT IN
(
SELECT MIN(ID)
FROM Table
GROUP BY Field1, Field2, Field3, ...
)
Run Code Online (Sandbox Code Playgroud)

以下fields是您要对重复行进行分组的列.

  • @ M1ke MySQL不允许从子查询引用的主表更新,但有一个解决方法; 将'FROM Table'更改为'FROM(SELECT*FROM Table)AS t1'将表存储在临时表中,以便更新主表. (5认同)
  • 很好.但是如果我们没有主键呢? (4认同)
  • 使用这种格式,我得到以下错误,有什么想法吗?“错误1093(HY000):您无法在FROM子句中指定目标表'Table'用于更新” (2认同)
  • @merdan,它适用于任何可排序的东西。例如以下是有效的`select min(id) from ( select newid() as id union select newid() as id ) as a` (2认同)

Cᴏʀ*_*ᴏʀʏ 26

这是我对它的一种扭曲,有一个可运行的例子.请注意,这仅适用于唯一的情况Id,并且您在其他列中具有重复值.

DECLARE @SampleData AS TABLE (Id int, Duplicate varchar(20))

INSERT INTO @SampleData
SELECT 1, 'ABC' UNION ALL
SELECT 2, 'ABC' UNION ALL
SELECT 3, 'LMN' UNION ALL
SELECT 4, 'XYZ' UNION ALL
SELECT 5, 'XYZ'

DELETE FROM @SampleData WHERE Id IN (
    SELECT Id FROM (
        SELECT 
            Id
            ,ROW_NUMBER() OVER (PARTITION BY [Duplicate] ORDER BY Id) AS [ItemNumber]
            -- Change the partition columns to include the ones that make the row distinct
        FROM 
            @SampleData
    ) a WHERE ItemNumber > 1 -- Keep only the first unique item
)

SELECT * FROM @SampleData
Run Code Online (Sandbox Code Playgroud)

结果如下:

Id          Duplicate
----------- ---------
1           ABC
3           LMN
4           XYZ
Run Code Online (Sandbox Code Playgroud)

不知道为什么这就是我首先想到的......绝对不是最简单的方法,但它有效.

  • @Fandango68:我相信我已经在帖子正文中解释了风险。复制和粘贴随机的 Internet 代码片段是一项危险的工作。非常欢迎您投票删除帖子,看看社区是否同意。 (5认同)
  • 这不会保留一份原件的副本。这也会删除原始内容。 (2认同)
  • 我们可以删除这篇文章吗,因为除非你测试并阅读评论,否则我很危险! (2认同)