从相当大的数据中删除重复行的最佳方法是什么 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 独特的柱子,以及 SELECTMIN (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

执行计划

此执行计划通常比接受的答案中的执行计划更简单、更高效,因为它不需要自连接。

Execution Plans

然而情况并非总是如此。一个地方 GROUP BY 解决方案可能是首选的情况是 哈希聚合 将优先选择流聚合。

ROW_NUMBER 解决方案总是给出几乎相同的计划,而 GROUP BY 策略更加灵活。

Execution Plans

可能有利于哈希聚合方法的因素是

  • 分区列上没有有用的索引
  • 组数相对较少,每组中的重复项相对较多

在第二种情况的极端版本​​中(如果每个组中都有很多重复项),也可以考虑简单地将行插入到新表中,然后 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, Column2Column3.

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)
  1. 创建具有相同结构的新空白表

  2. 像这样执行查询

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) > 1
    
  3. 然后执行这个查询

    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
                     ))
               );
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top