Sql 如何删除除一条记录外的大量重复数据,但没有相同的列/主键
我想在没有主键的情况下删除表中的重复行 定义为非标准化DB 我的问题是-我的表有大约5.4亿条记录。以前我使用CTE删除记录,但这需要8个多小时的时间。我想优化查询。 例如:如果表1的数据如下所示Sql 如何删除除一条记录外的大量重复数据,但没有相同的列/主键,sql,sql-server,database,Sql,Sql Server,Database,我想在没有主键的情况下删除表中的重复行 定义为非标准化DB 我的问题是-我的表有大约5.4亿条记录。以前我使用CTE删除记录,但这需要8个多小时的时间。我想优化查询。 例如:如果表1的数据如下所示 ID FNAME LNAME 1 AAA CCC 2 BBB DDD 1 AAA CCC 2 BBB DDD 1 AAA CCC 2 BBB DDD 3 BCB DGD 删除重复的行,并使用单个查询将数据保存到表中,如下所示 ID FNAME LNAME 1 AAA CCC 2 BBB DDD 3
ID FNAME LNAME
1 AAA CCC
2 BBB DDD
1 AAA CCC
2 BBB DDD
1 AAA CCC
2 BBB DDD
3 BCB DGD
删除重复的行,并使用单个查询将数据保存到表中,如下所示
ID FNAME LNAME
1 AAA CCC
2 BBB DDD
3 BCB DGD
之前我应用了这种类型的查询-
;with TBLCTE(EmpID,Ranking)
AS
(
select
EmpID,
Ranking = DENSE_RANK() over (PARTITION BY EmpID order by newID())
from @TBL
)
delete from TBLCTE where Ranking > 1
select * from @TBL order by EmpID
但这需要花费太多的时间
我想要一个解决方案来满足以下条件:
没有主键或相同的列
数据超过5.4亿,查询删除记录所需的时间应更短。
试试这个
Select * into #temp from @TBL
create nonclustered index Temp_Index on #temp(EmpId)
;with TBLCTE(EmpID,Ranking)
AS
(
select
EmpID,
Ranking = DENSE_RANK() over (PARTITION BY EmpID order by newID())
from #temp
)
delete from #temp where Ranking > 1
truncate table @TBL
insert into @TBL
Select * from #temp
WITH TempId AS (
SELECT *,
row_number() OVER(PARTITION BY ID, FNAME,LNAME ORDER BY ID) AS [Num]
FROM Employee)
DELETE TempId WHERE [Num] > 1
Select * from Employee
您必须在较小的数据块中删除重复的数据-创建一个循环并逐个处理数据块。最小的区块定义为1条唯一记录的所有副本。。。 您的语句耗时太长,因为它必须在内存中创建整个快照。您可以这样做: 1.在临时表中插入不同的记录 2.截断原始表
SELECT DISTINCT T.*
INTO #TEMPTABLE
FROM T;
TRUNCATE TABLE T;
INSERT INTO t
SELECT tt.*
FROM #temptable tt;
3.将记录插回原始表
SELECT DISTINCT T.*
INTO #TEMPTABLE
FROM T;
TRUNCATE TABLE T;
INSERT INTO t
SELECT tt.*
FROM #temptable tt;
或
1.在临时表中插入不同的记录
2.放下原来的桌子
SELECT DISTINCT T.*
INTO #TEMPTABLE
FROM T;
TRUNCATE TABLE T;
INSERT INTO t
SELECT tt.*
FROM #temptable tt;
3.重命名临时表
SELECT DISTINCT *
INTO NewTable
FROM OldTable;
DROP TABLE OldTable;
EXEC sp_rename 'OldTable', 'NewTable'
您可以用一种更快的方式来获取不同的记录来代替SELECT DISTINCT。但程序仍然保持不变
下面是Paul White使用递归CTE实现的超快速区分。请参阅以供参考:
CREATE CLUSTERED INDEX c ON dbo.T(EmpID);
WITH RecursiveCTE
AS (
SELECT data = MIN(T.data)
FROM dbo.Test T
UNION ALL
SELECT R.data
FROM (
-- A cunning way to use TOP in the recursive part of a CTE :)
SELECT T.data,
rn = ROW_NUMBER() OVER (ORDER BY T.data)
FROM dbo.Test T
JOIN RecursiveCTE R
ON R.data < T.data
) R
WHERE R.rn = 1
)
SELECT *
FROM RecursiveCTE
OPTION (MAXRECURSION 0);
按块从旧表传输到新表数据:
DECLARE @ChunkSize INT = 1000;
WHILE (EXISTS(
SELECT TOP(1)1 FROM OldTable ot
WHERE
NOT EXISTS(
SELECT TOP(1) 1
FROM NewTable nt
WHERE
nt.FNAME = ot.FNAME AND nt.LNAME = ot.LNAME)))
BEGIN
BEGIN TRANSACTION;
INSERT INTO NewTable(FNAME, LNAME)
SELECT DISTINCT TOP(@ChunkSize)
FNAME, LNAME
FROM
OldTable ot
WHERE
NOT EXISTS(
SELECT TOP(1) 1
FROM NewTable nt
WHERE
nt.FNAME = ot.FNAME AND
nt.LNAME = ot.LNAME);
COMMIT TRANSACTION;
END;
清除源表
TRUNCATE TABLE OldTable;
将数据传输回旧表
WHILE (EXISTS(
SELECT TOP(1)1 FROM NewTable nt
WHERE
NOT EXISTS(
SELECT TOP(1) 1
FROM OldTable ot
WHERE
nt.FNAME = ot.FNAME AND nt.LNAME = ot.LNAME)))
BEGIN
BEGIN TRANSACTION;
INSERT INTO OldTable(FNAME, LNAME)
SELECT DISTINCT TOP(@ChunkSize)
FNAME, LNAME
FROM
NewTable nt
WHERE
NOT EXISTS(
SELECT TOP(1) 1
FROM OldTable ot
WHERE
nt.FNAME = ot.FNAME AND
nt.LNAME = ot.LNAME);
COMMIT TRANSACTION;
END;
清除传输表:
TRUNCATE TABLE NewTable;
SELECT TOP(1000) * FROM OldTable
结果:
我认为使用SSI是将数据传输到另一个表然后返回的最快方式
尝试用鼠标右键单击数据库,任务->导入数据。然后选择与数据源相同的数据库并选择写入自定义查询。。。选项:选择DISTINCT FNAME,LNAME FROM Old Table并选择NewTable作为目标表
最后,运行导入尝试以下操作
Select * into #temp from @TBL
create nonclustered index Temp_Index on #temp(EmpId)
;with TBLCTE(EmpID,Ranking)
AS
(
select
EmpID,
Ranking = DENSE_RANK() over (PARTITION BY EmpID order by newID())
from #temp
)
delete from #temp where Ranking > 1
truncate table @TBL
insert into @TBL
Select * from #temp
WITH TempId AS (
SELECT *,
row_number() OVER(PARTITION BY ID, FNAME,LNAME ORDER BY ID) AS [Num]
FROM Employee)
DELETE TempId WHERE [Num] > 1
Select * from Employee
在Fiddle中找到解决方案你说的540亿是什么意思?你能添加一些索引吗?@weweweshemenance或使用SELECT-INTO,删除原始表,然后重命名新创建的表。我从未尝试过,但使用ignore\u-dup\u-key-index+SELECT-INTO或bcp之类的新表会起作用吗?为了成批处理,我们需要一个唯一标识每个记录的主键或列,但将此列及其数据添加到表中可能会再次花费太长时间。有什么想法吗?我们试过了,这也花费了太多的时间:对超过5亿行执行select distinct听起来相当重,由group而不是distinct使用,这会有区别吗?你可以找到另一种方法插入不同的记录,但想法仍然是一样的。嗨..你能告诉我子查询在这种情况下是如何工作的吗。我不理解这里“不存在”的用法。谢谢你的帮助你好。我们检查源表中不存在于目标表中的现有记录,然后按块传输它们。Exists检查查询返回的结果。我同意,exist将检查源和目标之间是否存在匹配的记录,并且不会再次插入这些记录。但哪里不存在1?这如何证明插入的合理性?1代表什么?不存在与存在相反。即。我们检查不存在符合条件的记录。1表示我们不需要SELECT语句中的任何字段。您可以在这里输入任何数字或字符串,而不是1,但我更喜欢使用1来保持简单。它取决于索引,但它应该比普通SELECT更快。您能为这两个表创建索引吗?