如何删除除一条没有相同列/主键的记录之外的大量重复数据

时间:2015-07-15 06:12:54

标签: sql sql-server database

我想在没有主键的情况下删除表中的重复行 已定义(非标准化DB)。

我的问题是 - 我的桌子有大约5.4亿条记录。以前我使用CTE删除记录但是花费的时间超过8小时。我想优化查询。 例如:如果我们有table1,数据如下,

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

但是花了太多时间。

我想要一个能够满足这些条件的解决方案:

  1. 没有主键或相同的列
  2. 数据超过5.4亿,查询应该花费更少的时间来删除记录。

5 个答案:

答案 0 :(得分:1)

通过块传输到OldTable的NewTable数据:

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;

将数据传回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

结果:

enter image description here

我认为使用SSIS是将数据传输到另一个表然后将其返回的最快方法。

尝试用鼠标右键单击数据库,任务 - >导入数据。然后选择相同的DB作为数据源并选择“Write custom query ...”选项:SELECT DISTINCT FNAME,LNAME FROM Old Table并选择NewTable作为目标表。

最后,运行导入

答案 1 :(得分:1)

试试这个

 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

在小提琴http://sqlfiddle.com/#!6/394a9/1

中找到解决方案

答案 2 :(得分:0)

试试这个

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

答案 3 :(得分:0)

您必须删除较小块中的重复数据 - 进行循环并相互处理块。最小块被定义为1个唯一记录的所有重复...
你的陈述花了这么长时间,因为它必须在内存中创建整个快照。

答案 4 :(得分:0)

你可以这样做:

1.将不同的记录插入临时表中。

2.Truncate原始表。

3.将记录插回原始表格。

SELECT DISTINCT T.*
INTO #TEMPTABLE
FROM T;

TRUNCATE TABLE T;

INSERT INTO t
    SELECT tt.*
    FROM #temptable tt;

OR

1.将不同的记录插入临时表中。

2.Drop原始表。

3.重命名临时表

SELECT DISTINCT *
INTO NewTable
FROM OldTable;

DROP TABLE OldTable;

EXEC sp_rename 'OldTable', 'NewTable'

您可以用更快的方式替换SELECT DISTINCT以获取不同的记录。但程序仍然保持不变。

这是使用Paul White的递归CTE的超快DISTINCT。请参阅this以获取参考:

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);