T-SQL: Deleting all duplicate rows but keeping one
Possible Duplicate:
SQL - How can I remove duplicate rows?
我有一个行数非常多的表。 不允许重复,但由于行如何创建的问题我知道这个表中有一些重复。
我需要从关键列的角度消除额外的行。 其他一些列的数据可能略有不同,但我并不关心。 我仍然需要保留其中一行。 SELECT DISTINCT将无法工作,因为它在所有列上运行,我需要根据键列抑制重复。
如何删除额外的行但仍保持有效?
您没有说明您使用的是哪个版本,但在SQL 2005及更高版本中,您可以使用带有OVER子句的公用表表达式。它有点像这样:
1 2 3 4 5 6
| 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 |
玩弄它,看看你得到了什么。
(编辑:为了有所帮助,有人编辑了CTE中的ORDER BY子句。要清楚,你可以按照你想要的任何顺序排序,它不一定是cte返回的列之一。事实上。 ,这里常见的用例是"foo,bar"是组标识符,"baz"是某种时间戳。??为了保持最新,你要做ORDER BY baz desc)
-
这将保留最后一个重复行或第一行?
-
刚刚回到这个答案并注意到问题:它将保留哪个骗局。如上所述,它将保留"第一"重复行,其中"第一"表示"根据baz的最低排序"。当然,如果您不确定要删除/保留的内容,请将删除变为选择并确保。比抱歉更安全。
-
如果在批处理中执行它,请不要忘记WITH之前的半冒号。事务msdn.microsoft.com/en-us/library/ms175972.aspx
-
@ Mike1234:这有点真实,但很草率。规则是前一个语句需要以分号结尾。通过将CTE作为批次中的第一件事来说服自己这一事实。
-
你真的需要在CTE中选择除行号之外的任何东西吗?
-
@SumGuy:不;行号就足够了。但我喜欢先把它作为选择来检查会受到什么影响。此外,我刚刚进行了快速测试,看起来SQL Server足够智能,不会向前传送不需要的列。我通过查看实际执行计划中的输出列列表确定了这一点,我选择了所有内容加上行号和行号;两者完全相同。
-
如果行数较多,则可能不建议使用DELETE(完全恢复也会导致事务日志填满)。最好做一个SELECT * INTO NewTable FROM cte,然后删除旧表。对于非常大的表,这将更快。
-
非常好;我用它来删除连续的重复行!
-
我有一个真正的重复行的情况(因此排序并不重要),它工作!
示例查询:
1 2 3 4 5 6 7
| DELETE FROM TABLE
WHERE ID NOT IN
(
SELECT MIN(ID)
FROM TABLE
GROUP BY Field1, Field2, Field3, ...
) |
此处fields是要在其上对重复行进行分组的列。
-
使用这种格式我得到以下错误,任何想法?"ERROR 1093(HY000):您无法在FROM子句中为更新指定目标表'Table'"
-
@ M1ke MySQL不允许从子查询引用的主表更新,但有一个解决方法;将'FROM Table'更改为'FROM(SELECT * FROM Table)AS t1'将表存储在临时表中,以便更新主表。
-
谢谢,我实际上在其他地方找到了相同的答案,但不记得在哪里 - 所以加1!
-
很好。但是如果我们没有主键呢?
-
如果primery键是guid,而不是整数
-
请注意以OP建议的"大量行"的方式使用DELETE。如果磁盘IO不是内存或内存优化表,则需要花费大量时间。此外,恢复模型很重要,如果设置为FULL,您可能会将大量数据写入事务日志,这会增加更多时间(如果不小心,可以填满您的驱动器)。分组/分钟或DISTINCT *可能是最安全的,但CTE / ROW_NUMBER是可接受的,并且可能是最快的,只要RAM允许它没有分页到磁盘(再次,另一个巨大的时间消费者)。
-
@merdan,它适用于任何可排序的东西。例如以下是有效的select min(id) from ( select newid() as id union select newid() as id ) as a
这是我对它的一种扭曲,有一个可运行的例子。请注意,这仅适用于Id唯一且您在其他列中具有重复值的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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 |
结果如下:
1 2 3 4 5
| Id Duplicate
----------- ---------
1 ABC
3 LMN
4 XYZ |
不知道为什么这就是我首先想到的......绝对不是最简单的方法,但它有效。
-
这不会保留一个重复的原件。这也会删除原件。
-
嗨@Sandy,你验证了吗?四年前我回答,我不记得我是否在真实数据上测试过它。
-
是的,我查看了真实数据。这也将删除原件。
-
@Sandy,我刚刚对一些示例数据测试了我的查询,它运行正常。有关可运行示例,请参阅我对此答案的编辑。我的想法可能是你没有正确应用ROW_NUMBER()功能。
-
哦。我期待甚至Id重复。因此,行将是(1,ABC),(1,ABC),(3,LMN),(3,LMN)。我正在为这种情况寻找答案。
-
让我们在聊天中继续讨论。
-
我们可以删除这篇文章,因为除非你测试和阅读评论,否则我的血液很危险!
-
@Fandango68:我相信我已经解释了帖子中的风险。复制和粘贴随机的Internet代码片段是一项危险的工作。非常欢迎您投票删除帖子以查看社区是否同意。