如何删除表中的重复行
我有一张桌子,上面说三列。没有主键,因此可以有重复的行。我只需要保留一个,然后删除其他。任何想法如何做到这一点是Sql Server?
解决方案
添加一个标识列作为代理主键,并使用它标识要删除的三行中的两行。
我会考虑在之后保留标识列,或者如果这是某种链接表,请在其他列上创建复合主键。
我将选择DISTINCT这些行,并将它们放入临时表中,然后删除源表,并从临时表中复制回数据。
编辑:现在带有代码片段!
INSERT INTO TABLE_2 SELECT DISTINCT * FROM TABLE_1 GO DELETE FROM TABLE_1 GO INSERT INTO TABLE_1 SELECT * FROM TABLE_2 GO
这是一个艰难的情况。在不知道特定情况(表大小等)的情况下,我认为我们最好的选择是添加一个Identity列,填充它,然后根据它删除。我们可以稍后删除该列,但我建议我们保留该列,因为在表中保留它确实是一件好事
当PK只是所有表列的一个子集时,以下示例同样适用。
(注意:我更喜欢插入另一个代理id列的方法。但是也许此解决方案也很方便。)
首先找到重复的行:
SELECT col1, col2, count(*) FROM t1 GROUP BY col1, col2 HAVING count(*) > 1
如果只有少数几个,则可以手动将其删除:
set rowcount 1 delete from t1 where col1=1 and col2=1
" rowcount"的值应为重复次数的n-1倍。在此示例中,有2个dulpics,因此rowcount为1. 如果获得多个重复的行,则必须为每个唯一的主键执行此操作。
如果重复很多,则将每个密钥复制一次到另一个表:
SELECT col1, col2, col3=count(*) INTO holdkey FROM t1 GROUP BY col1, col2 HAVING count(*) > 1
然后复制密钥,但消除重复项。
SELECT DISTINCT t1.* INTO holddups FROM t1, holdkey WHERE t1.col1 = holdkey.col1 AND t1.col2 = holdkey.col2
在键中,我们现在拥有唯一的键。检查是否没有任何结果:
SELECT col1, col2, count(*) FROM holddups GROUP BY col1, col2
从原始表中删除重复项:
DELETE t1 FROM t1, holdkey WHERE t1.col1 = holdkey.col1 AND t1.col2 = holdkey.col2
插入原始行:
INSERT t1 SELECT * FROM holddups
顺便说一句,为了完整性:在Oracle中,我们可以使用一个隐藏字段(行):
DELETE FROM our_table WHERE rowid not in (SELECT MIN(rowid) FROM our_table GROUP BY column1, column2, column3... ;
请参阅:Microsoft知识站点
清理完当前混乱之后,我们可以添加一个主键,其中包含表中的所有字段。这样可以避免我们再次陷入困境。
当然,此解决方案很可能会破坏现有代码。那也必须处理。
我不确定这是否适用于DELETE语句,但这是查找重复行的一种方式:
SELECT * FROM myTable t1, myTable t2 WHERE t1.field = t2.field AND t1.id > t2.id
我不确定是否可以将" SELECT"更改为" DELETE"(有人想让我知道吗?),但是即使不能,也可以将其转换为子查询。
这是我问这个问题时使用的方法-
DELETE MyTable FROM MyTable LEFT OUTER JOIN ( SELECT MIN(RowId) as RowId, Col1, Col2, Col3 FROM MyTable GROUP BY Col1, Col2, Col3 ) as KeepRows ON MyTable.RowId = KeepRows.RowId WHERE KeepRows.RowId IS NULL
可以将主键标识字段添加到表中吗?
Manrico Corazzi我专门研究Oracle,而不是MS SQL,因此我们必须告诉我这是否可以提高性能:-
- 与第一步相同-将不同的值插入TABLE1中的TABLE2中。
- 删除TABLE1. (丢弃应该比我认为的快,就像截断要比删除要快一样多)。
- 将TABLE2重命名为TABLE1(节省时间,因为我们要重命名对象而不是将数据从一个表复制到另一个表)。
这是测试数据的另一种方法
create table #table1 (colWithDupes1 int, colWithDupes2 int) insert into #table1 (colWithDupes1, colWithDupes2) Select 1, 2 union all Select 1, 2 union all Select 2, 2 union all Select 3, 4 union all Select 3, 4 union all Select 3, 4 union all Select 4, 2 union all Select 4, 2 select * from #table1 set rowcount 1 select 1 while @@rowcount > 0 delete #table1 where 1 < (select count(*) from #table1 a2 where #table1.colWithDupes1 = a2.colWithDupes1 and #table1.colWithDupes2 = a2.colWithDupes2 ) set rowcount 0 select * from #table1
这是使用通用表表达式CTE的一种方法。它没有循环,没有新列或者任何东西,并且不会引起任何不必要的触发器触发(由于deletes + inserts)。
受本文启发。
CREATE TABLE #temp (i INT) INSERT INTO #temp VALUES (1) INSERT INTO #temp VALUES (1) INSERT INTO #temp VALUES (2) INSERT INTO #temp VALUES (3) INSERT INTO #temp VALUES (3) INSERT INTO #temp VALUES (4) SELECT * FROM #temp ; WITH [#temp+rowid] AS (SELECT ROW_NUMBER() OVER (ORDER BY i ASC) AS ROWID, * FROM #temp) DELETE FROM [#temp+rowid] WHERE rowid IN (SELECT MIN(rowid) FROM [#temp+rowid] GROUP BY i HAVING COUNT(*) > 1) SELECT * FROM #temp DROP TABLE #temp
怎么样:
select distinct * into #t from duplicates_tbl truncate duplicates_tbl insert duplicates_tbl select * from #t drop table #t
那这个解决方案呢:
首先,我们执行以下查询:
select 'set rowcount ' + convert(varchar,COUNT(*)-1) + ' delete from MyTable where field=''' + field +'''' + ' set rowcount 0' from mytable group by field having COUNT(*)>1
然后我们只需要执行返回的结果集
set rowcount 3 delete from Mytable where field='foo' set rowcount 0 .... .... set rowcount 5 delete from Mytable where field='bar' set rowcount 0
当我们只有一列时,我已经处理了这种情况,但是很容易将相同的方法应用于多于一列。如果我们要我发布代码,请告诉我。