Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/85.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 如何删除除一条记录外的大量重复数据,但没有相同的列/主键_Sql_Sql Server_Database - Fatal编程技术网

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

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

我想在没有主键的情况下删除表中的重复行 定义为非标准化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 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更快。您能为这两个表创建索引吗?