【发布时间】:2008-09-18 11:35:27
【问题描述】:
我有一张有 3 列的表格。没有主键,所以可以有重复的行。我只需要保留一个并删除其他的。知道如何做到这一点是 Sql Server 吗?
【问题讨论】:
标签: sql sql-server database
我有一张有 3 列的表格。没有主键,所以可以有重复的行。我只需要保留一个并删除其他的。知道如何做到这一点是 Sql Server 吗?
【问题讨论】:
标签: sql sql-server database
我会选择 DISTINCT 行并将它们放入临时表中,然后删除源表并从临时表中复制回数据。 编辑: 现在使用代码 sn-p!
INSERT INTO TABLE_2
SELECT DISTINCT * FROM TABLE_1
GO
DELETE FROM TABLE_1
GO
INSERT INTO TABLE_1
SELECT * FROM TABLE_2
GO
【讨论】:
添加一个标识列作为代理主键,并使用它来标识要删除的三行中的两行。
我会考虑在之后保留标识列,或者如果这是某种链接表,请在其他列上创建复合主键。
【讨论】:
当您的 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 个重复项,因此行数为 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 中,您可以使用一个隐藏字段(rowid):
DELETE FROM our_table
WHERE rowid not in
(SELECT MIN(rowid)
FROM our_table
GROUP BY column1, column2, column3... ;
【讨论】:
这是我使用的方法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
【讨论】:
这是一种使用公用表表达式 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
【讨论】:
这是一个艰难的情况。在不知道您的特定情况(表大小等)的情况下,我认为您最好的方法是添加一个标识列,填充它,然后根据它删除。您可以稍后删除该列,但我建议您应该保留它,因为它确实是一个很好的东西在表格中
【讨论】:
清理完当前的混乱后,您可以添加一个包含表中所有字段的主键。这将使您不再陷入困境。 当然,这个解决方案可以很好地破坏现有代码。这也必须处理。
【讨论】:
你能在表中添加一个主键标识字段吗?
【讨论】:
Manrico Corazzi - 我专攻 Oracle,而不是 MS SQL,所以您必须告诉我这是否可以提高性能:-
【讨论】:
这是另一种方式,带有测试数据
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
【讨论】:
这个解决方案怎么样:
首先执行以下查询:
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
当您只有一列时,我已经处理过这种情况,但是很容易将相同的方法应用于多列。如果您希望我发布代码,请告诉我。
【讨论】:
怎么样:
select distinct * into #t from duplicates_tbl
truncate duplicates_tbl
insert duplicates_tbl select * from #t
drop table #t
【讨论】:
我不确定这是否适用于 DELETE 语句,但这是一种查找重复行的方法:
SELECT *
FROM myTable t1, myTable t2
WHERE t1.field = t2.field AND t1.id > t2.id
我不确定你是否可以将“SELECT”更改为“DELETE”(有人想让我知道吗?),但即使你不能,你也可以它进入一个子查询。
【讨论】: