如何删除表中的重复行

时间:2008-09-18 11:35:27

标签: sql sql-server database

我有一张表有3列的表。没有主键,因此可能存在重复的行。我需要保留一个并删除其他人。任何想法如何做到这一点是Sql Server?

13 个答案:

答案 0 :(得分:23)

我选择DISTINCT行并将它们放入临时表中,然后删除源表并从temp中复制数据。 编辑:现在使用代码段!

INSERT INTO TABLE_2 
SELECT DISTINCT * FROM TABLE_1
GO
DELETE FROM TABLE_1
GO
INSERT INTO TABLE_1
SELECT * FROM TABLE_2
GO

答案 1 :(得分:7)

添加标识列以充当代理主键,并使用此标识来标识要删除的三行中的两行。

我会考虑在之后保留标识列,或者如果这是某种链接表,则在其他列上创建复合主键。

答案 2 :(得分:7)

当您的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个dulpicates,因此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

btw和完整性:在Oracle中有一个可以使用的隐藏字段(rowid):

DELETE FROM our_table
WHERE rowid not in
(SELECT MIN(rowid)
FROM our_table
GROUP BY column1, column2, column3... ;

请参阅:Microsoft Knowledge Site

答案 3 :(得分:4)

以下是我使用的方法when I asked this question -

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

答案 4 :(得分:4)

这是使用公用表表达式CTE的方法。它不涉及循环,没有新列或任何东西,并且不会导致任何不需要的触发器触发(由于删除+插入)。

this article的启发。

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   

答案 5 :(得分:2)

这是一个艰难的情况。在不知道您的特定情况(表格大小等)的情况下,我认为您最好的方法是添加一个标识列,填充它然后根据它删除。您可以稍后删除该列,但我建议您保留它,因为它在表中确实是一件好事

答案 6 :(得分:0)

清理当前的混乱后,您可以添加包含表中所有字段的主键。这将使你不再陷入困境。 当然,这种解决方案很可能破坏现有代码。这也必须处理。

答案 7 :(得分:0)

您可以在表格中添加主键标识字段吗?

答案 8 :(得分:0)

Manrico Corazzi - 我专注于Oracle,而不是MS SQL,所以你必须告诉我这是否可以提升性能: -

  1. 保留与第一步相同的内容 - 将不同的值插入TABLE1中的TABLE2。
  2. 删除TABLE1。 (Drop应该比我假设的更快,就像截断比删除更快)。
  3. 将TABLE2重命名为TABLE1(节省时间,因为您重命名对象而不是将数据从一个表复制到另一个表)。

答案 9 :(得分:0)

这是另一种方式,包含测试数据

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

答案 10 :(得分:0)

怎么样:

select distinct * into #t from duplicates_tbl

truncate duplicates_tbl

insert duplicates_tbl select * from #t

drop table #t

答案 11 :(得分:0)

这个解决方案怎么样:

首先执行以下查询:

  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

当你只有一个专栏的时候,我已经处理了这个案例,但是很容易适应相同的方法而不是一列。如果您希望我发布代码,请告诉我。

答案 12 :(得分:-1)

我不确定这是否适用于DELETE语句,但这是一种查找重复行的方法:

 SELECT *
 FROM myTable t1, myTable t2
 WHERE t1.field = t2.field AND t1.id > t2.id

我不确定你是否可以将“SELECT”更改为“DELETE”(有人想让我知道吗?),但即使你不能,也可以它进入子查询。