문제

상당히 큰 데이터에서 중복 행을 제거하는 가장 좋은 방법은 무엇입니까? 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
도움이 되었습니까?

해결책

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

실행 계획

이에 대한 실행 계획은 자체 조인이 필요하지 않기 때문에 허용되는 답변보다 더 간단하고 효율적인 경우가 많습니다.

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

포스트그레스:

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)

SQL Server 테이블에서 중복 행을 삭제하려면 CTE를 선호합니다.

이 기사를 따르는 것이 좋습니다. ::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)

정확히 중복된 행을 삭제하는 Quick and Dirty(작은 테이블의 경우):

select  distinct * into t2 from t1;
delete from t1;
insert into t1 select *  from t2;
drop table t2;

나는 내부 조인보다 하위 쿼리\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

특별한 상황에서 작동하기 때문에 내 솔루션을 공유하고 싶다고 생각했습니다.제 경우에는 중복 값이 ​​있는 테이블에 외래 키가 없었습니다(값이 다른 DB에서 중복되었기 때문입니다).

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

그렇다면 중복된 데이터를 어떻게 삭제할 수 있습니까?

먼저 다음 코드를 사용하여 해당 테이블에 ID 열을 삽입합니다.

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
    )

200만 개의 테이블에서 30초가 조금 넘는 시간에 100만 개의 행을 삭제했습니다(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 무시(중복 값이 ​​있을 경우 수행할 작업에 대한 옵션도 있습니다.무시, 인터럽트 또는 sth) 중복 값.따라서 중복된 행이 없는 동일한 테이블이 있습니다. 고유 인덱스를 원하지 않으면 데이터 전송 후 삭제할 수 있습니다..

특히 더 큰 테이블의 경우 모든 데이터를 새로운 고유 인덱스 테이블로 신속하게 전송하기 위해 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 서버에서 작동하므로 언급하겠습니다.종종 1~2개의 중복 항목만 있고 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