题
从相当大的数据中删除重复行的最佳方法是什么 SQL Server
表(即300,000+ 行)?
当然,由于存在 RowID
身份字段。
我的桌子
RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null
解决方案
假设没有空值,你 GROUP BY
独特的柱子,以及 SELECT
这 MIN (or MAX)
RowId 作为要保留的行。然后,删除所有没有行 ID 的内容:
DELETE 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
如果您有 GUID 而不是整数,您可以替换
MIN(RowId)
和
CONVERT(uniqueidentifier, MIN(CONVERT(char(36), MyGuidColumn)))
其他提示
另一种可能的方法是
;
--Ensure that any immediately preceding statement is terminated with a semicolon above
WITH cte
AS (SELECT ROW_NUMBER() OVER (PARTITION BY Col1, Col2, Col3
ORDER BY ( SELECT 0)) RN
FROM #MyTable)
DELETE FROM cte
WHERE RN > 1;
我在用 ORDER BY (SELECT 0)
如上所述,因为在出现平局时保留哪一行是任意的。
为了保留最新的 RowID
例如,您可以使用订单 ORDER BY RowID DESC
执行计划
此执行计划通常比接受的答案中的执行计划更简单、更高效,因为它不需要自连接。
然而情况并非总是如此。一个地方 GROUP BY
解决方案可能是首选的情况是 哈希聚合 将优先选择流聚合。
这 ROW_NUMBER
解决方案总是给出几乎相同的计划,而 GROUP BY
策略更加灵活。
可能有利于哈希聚合方法的因素是
- 分区列上没有有用的索引
- 组数相对较少,每组中的重复项相对较多
在第二种情况的极端版本中(如果每个组中都有很多重复项),也可以考虑简单地将行插入到新表中,然后 TRUNCATE
- 与删除很大比例的行相比,将原始数据复制回来以最大限度地减少日志记录。
有一篇很好的文章 删除重复项 在 Microsoft 支持网站上。这是相当保守的 - 他们让你在单独的步骤中完成所有事情 - 但它应该适用于大型表。
我过去曾使用自连接来执行此操作,尽管它可能可以使用 HAVING 子句进行美化:
DELETE dupes
FROM MyTable dupes, MyTable fullTable
WHERE dupes.dupField = fullTable.dupField
AND dupes.secondDupField = fullTable.secondDupField
AND dupes.uniqueField > fullTable.uniqueField
以下查询对于删除重复行很有用。本例中的表有 ID
作为标识列,并且具有重复数据的列是 Column1
, Column2
和 Column3
.
DELETE FROM TableName
WHERE ID NOT IN (SELECT MAX(ID)
FROM TableName
GROUP BY Column1,
Column2,
Column3
/*Even if ID is not null-able SQL Server treats MAX(ID) as potentially
nullable. Because of semantics of NOT IN (NULL) including the clause
below can simplify the plan*/
HAVING MAX(ID) IS NOT NULL)
以下脚本显示了使用 GROUP BY
, HAVING
, ORDER BY
在一个查询中,并返回包含重复列及其计数的结果。
SELECT YourColumnName,
COUNT(*) TotalCount
FROM YourTableName
GROUP BY YourColumnName
HAVING COUNT(*) > 1
ORDER BY COUNT(*) DESC
delete t1
from table t1, table t2
where t1.columnA = t2.columnA
and t1.rowid>t2.rowid
Postgres:
delete
from table t1
using table t2
where t1.columnA = t2.columnA
and t1.rowid > t2.rowid
DELETE LU
FROM (SELECT *,
Row_number()
OVER (
partition BY col1, col1, col3
ORDER BY rowid DESC) [Row]
FROM mytable) LU
WHERE [row] > 1
这将删除除第一行之外的重复行
DELETE
FROM
Mytable
WHERE
RowID NOT IN (
SELECT
MIN(RowID)
FROM
Mytable
GROUP BY
Col1,
Col2,
Col3
)
参考 (http://www.codeproject.com/Articles/157977/Remove-Duplicate-Rows-from-a-Table-in-SQL-Server)
我更喜欢 CTE 从 sql server 表中删除重复行
强烈建议关注这篇文章::http://codaffection.com/sql-server-article/delete-duplicate-rows-in-sql-server/
通过保持原创
WITH CTE AS
(
SELECT *,ROW_NUMBER() OVER (PARTITION BY col1,col2,col3 ORDER BY col1,col2,col3) AS RN
FROM MyTable
)
DELETE FROM CTE WHERE RN<>1
不保留原来的
WITH CTE AS
(SELECT *,R=RANK() OVER (ORDER BY col1,col2,col3)
FROM MyTable)
DELETE CTE
WHERE R IN (SELECT R FROM CTE GROUP BY R HAVING COUNT(*)>1)
快速而肮脏地删除精确的重复行(对于小表):
select distinct * into t2 from t1;
delete from t1;
insert into t1 select * from t2;
drop table t2;
与内连接相比,我更喜欢 subquery\having count(*) > 1 解决方案,因为我发现它更容易阅读,并且很容易变成 SELECT 语句来验证在运行之前将删除哪些内容。
--DELETE FROM table1
--WHERE id IN (
SELECT MIN(id) FROM table1
GROUP BY col1, col2, col3
-- could add a WHERE clause here to further filter
HAVING count(*) > 1
--)
获取重复行:
SELECT
name, email, COUNT(*)
FROM
users
GROUP BY
name, email
HAVING COUNT(*) > 1
删除重复行:
DELETE users
WHERE rowid NOT IN
(SELECT MIN(rowid)
FROM users
GROUP BY name, email);
SELECT DISTINCT *
INTO tempdb.dbo.tmpTable
FROM myTable
TRUNCATE TABLE myTable
INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable
DROP TABLE tempdb.dbo.tmpTable
我想我应该分享我的解决方案,因为它在特殊情况下有效。在我的情况下,具有重复值的表没有外键(因为这些值是从另一个数据库复制的)。
begin transaction
-- create temp table with identical structure as source table
Select * Into #temp From tableName Where 1 = 2
-- insert distinct values into temp
insert into #temp
select distinct *
from tableName
-- delete from source
delete from tableName
-- insert into source from temp
insert into tableName
select *
from #temp
rollback transaction
-- if this works, change rollback to commit and execute again to keep you changes!!
附:当处理这样的事情时,我总是使用事务,这不仅确保所有内容作为一个整体执行,而且还允许我在不冒任何风险的情况下进行测试。但当然,无论如何你都应该进行备份以确保......
使用 CTE。这个想法是加入一个或多个形成重复记录的列,然后删除您喜欢的任何列:
;with cte as (
select
min(PrimaryKey) as PrimaryKey
UniqueColumn1,
UniqueColumn2
from dbo.DuplicatesTable
group by
UniqueColumn1, UniqueColumn1
having count(*) > 1
)
delete d
from dbo.DuplicatesTable d
inner join cte on
d.PrimaryKey > cte.PrimaryKey and
d.UniqueColumn1 = cte.UniqueColumn1 and
d.UniqueColumn2 = cte.UniqueColumn2;
可以在粘贴的链接中找到另一个简单的解决方案 这里. 。这个方法很容易掌握,并且对于大多数类似的问题似乎都是有效的。虽然它是针对 SQL Server 的,但所使用的概念是完全可以接受的。
以下是链接页面的相关部分:
考虑这个数据:
EMPLOYEE_ID ATTENDANCE_DATE
A001 2011-01-01
A001 2011-01-01
A002 2011-01-01
A002 2011-01-01
A002 2011-01-01
A003 2011-01-01
那么我们该如何删除这些重复的数据呢?
首先,使用以下代码在该表中插入标识列:
ALTER TABLE dbo.ATTENDANCE ADD AUTOID INT IDENTITY(1,1)
使用下面的代码可以解决它:
DELETE FROM dbo.ATTENDANCE WHERE AUTOID NOT IN (SELECT MIN(AUTOID) _
FROM dbo.ATTENDANCE GROUP BY EMPLOYEE_ID,ATTENDANCE_DATE)
这个查询对我来说表现出了非常好的性能:
DELETE tbl
FROM
MyTable tbl
WHERE
EXISTS (
SELECT
*
FROM
MyTable tbl2
WHERE
tbl2.SameValue = tbl.SameValue
AND tbl.IdUniqueValue < tbl2.IdUniqueValue
)
它在 30 秒多一点的时间内从 2M 的表中删除了 1M 行(50% 重复)
这是另一篇关于 删除重复项.
它讨论了为什么它很难:”SQL是基于关系代数的,关系代数中不能出现重复,因为集合中不允许出现重复。"
临时表解决方案,以及两个 mysql 示例。
将来您将在数据库级别或从应用程序角度阻止它。我建议使用数据库级别,因为您的数据库应该负责维护引用完整性,开发人员只会造成问题;)
哦没问题。使用临时表。如果您想要一个“有效”的、性能不是很好的语句,您可以使用:
DELETE FROM MyTable WHERE NOT RowID IN
(SELECT
(SELECT TOP 1 RowID FROM MyTable mt2
WHERE mt2.Col1 = mt.Col1
AND mt2.Col2 = mt.Col2
AND mt2.Col3 = mt.Col3)
FROM MyTable mt)
基本上,对于表中的每一行,子选择都会查找与所考虑的行完全相同的所有行的顶部 RowID。因此,您最终会得到代表“原始”非重复行的 RowID 列表。
我有一个表,需要在其中保留不重复的行。我不确定速度或效率。
DELETE FROM myTable WHERE RowID IN (
SELECT MIN(RowID) AS IDNo FROM myTable
GROUP BY Col1, Col2, Col3
HAVING COUNT(*) = 2 )
另一种方法是 创建一个新的 具有相同字段的表和 具有唯一索引. 。然后 将所有数据从旧表移动到新表. 。自动 SQL SERVER 忽略(还有一个关于如果存在重复值该怎么办的选项:忽略、中断或其他)重复值。所以我们有同一个表,没有重复的行。 如果您不需要唯一索引,传输数据后您可以将其删除.
尤其 对于更大的桌子 您可以使用 DTS(用于导入/导出数据的 SSIS 包)将所有数据快速传输到新的唯一索引表。对于 700 万行,只需几分钟。
用这个
WITH tblTemp as
(
SELECT ROW_NUMBER() Over(PARTITION BY Name,Department ORDER BY Name)
As RowNumber,* FROM <table_name>
)
DELETE FROM tblTemp where RowNumber >1
通过使用下面的查询,我们可以根据单列或多列删除重复记录。下面的查询是根据两列进行删除。表名称是: testing
和列名称 empno,empname
DELETE FROM testing WHERE empno not IN (SELECT empno FROM (SELECT empno, ROW_NUMBER() OVER (PARTITION BY empno ORDER BY empno)
AS [ItemNumber] FROM testing) a WHERE ItemNumber > 1)
or empname not in
(select empname from (select empname,row_number() over(PARTITION BY empno ORDER BY empno)
AS [ItemNumber] FROM testing) a WHERE ItemNumber > 1)
创建具有相同结构的新空白表
像这样执行查询
INSERT INTO tc_category1 SELECT * FROM tc_category GROUP BY category_id, application_id HAVING count(*) > 1
然后执行这个查询
INSERT INTO tc_category1 SELECT * FROM tc_category GROUP BY category_id, application_id HAVING count(*) = 1
这是删除重复记录的最简单方法
DELETE FROM tblemp WHERE id IN
(
SELECT MIN(id) FROM tblemp
GROUP BY title HAVING COUNT(id)>1
)
http://askme.indianyouth.info/details/how-to-dumplicate-record-from-table-in-using-sql-105
我想提一下这种方法,它很有帮助,并且适用于所有 SQL 服务器:通常只有一到两个重复项,并且 ID 和重复项的数量是已知的。在这种情况下:
SET ROWCOUNT 1 -- or set to number of rows to be deleted
delete from myTable where RowId = DuplicatedID
SET ROWCOUNT 0
从应用程序级别(不幸的是)。我同意防止重复的正确方法是在数据库级别通过使用唯一索引,但在 SQL Server 2005 中,索引只允许为 900 字节,而我的 varchar(2048) 字段则打破了这一点。
我不知道它的性能如何,但我认为您可以编写一个触发器来强制执行此操作,即使您无法直接使用索引来执行此操作。就像是:
-- given a table stories(story_id int not null primary key, story varchar(max) not null)
CREATE TRIGGER prevent_plagiarism
ON stories
after INSERT, UPDATE
AS
DECLARE @cnt AS INT
SELECT @cnt = Count(*)
FROM stories
INNER JOIN inserted
ON ( stories.story = inserted.story
AND stories.story_id != inserted.story_id )
IF @cnt > 0
BEGIN
RAISERROR('plagiarism detected',16,1)
ROLLBACK TRANSACTION
END
另外,varchar(2048) 对我来说听起来很可疑(生活中有些东西是 2048 字节,但这是相当罕见的);它真的不应该是 varchar(max) 吗?
DELETE
FROM
table_name T1
WHERE
rowid > (
SELECT
min(rowid)
FROM
table_name T2
WHERE
T1.column_name = T2.column_name
);
CREATE TABLE car(Id int identity(1,1), PersonId int, CarId int)
INSERT INTO car(PersonId,CarId)
VALUES(1,2),(1,3),(1,2),(2,4)
--SELECT * FROM car
;WITH CTE as(
SELECT ROW_NUMBER() over (PARTITION BY personid,carid order by personid,carid) as rn,Id,PersonID,CarId from car)
DELETE FROM car where Id in(SELECT Id FROM CTE WHERE rn>1)
我想预览要删除的行并控制要保留哪些重复行。看 http://developer.azurewebsites.net/2014/09/better-sql-group-by-find-duplicate-data/
with MYCTE as (
SELECT ROW_NUMBER() OVER (
PARTITION BY DuplicateKey1
,DuplicateKey2 -- optional
ORDER BY CreatedAt -- the first row among duplicates will be kept, other rows will be removed
) RN
FROM MyTable
)
DELETE FROM MYCTE
WHERE RN > 1
DELETE
FROM MyTable
WHERE NOT EXISTS (
SELECT min(RowID)
FROM Mytable
WHERE (SELECT RowID
FROM Mytable
GROUP BY Col1, Col2, Col3
))
);