SQL Server에서 중복 행을 삭제하는 방법은 무엇입니까?


415

존재 하지 않는 중복 행을 어떻게 삭제unique row id 합니까?

내 테이블은

col1  col2 col3 col4 col5 col6 col7
john  1    1    1    1    1    1 
john  1    1    1    1    1    1
sally 2    2    2    2    2    2
sally 2    2    2    2    2    2

중복 제거 후 다음과 같이 남겨두고 싶습니다.

john  1    1    1    1    1    1
sally 2    2    2    2    2    2

몇 가지 쿼리를 시도했지만 원하는 결과를 얻지 못하기 때문에 행 ID가 있어야한다고 생각합니다. 예를 들면 다음과 같습니다.

DELETE
FROM table
WHERE col1 IN (
    SELECT id
    FROM table
    GROUP BY id
    HAVING (COUNT(col1) > 1)
)

5
이것은 첫 번째 링크의 속임수가 아닙니다. 이 질문에는 행 ID가없고 링크 된 질문에는 행 ID가 있습니다. 매우 다릅니다.
Alien Technology

MAX / MIN과 같은 집계 함수를 갖도록 'SELECT id FROM table GROUP BY id HAVING'을 변경하면 작동합니다.
엉망 업

답변:


785

CTE는 같은 I와 ROW_NUMBER결합 된 두 그러므로 우리 단지 변경, 삭제 (또는 갱신)하는 행을 볼 수 있도록로 DELETE FROM CTE...SELECT * FROM CTE:

WITH CTE AS(
   SELECT [col1], [col2], [col3], [col4], [col5], [col6], [col7],
       RN = ROW_NUMBER()OVER(PARTITION BY col1 ORDER BY col1)
   FROM dbo.Table1
)
DELETE FROM CTE WHERE RN > 1

DEMO (결과는 다릅니다. 오타 때문이라고 가정합니다)

COL1    COL2    COL3    COL4    COL5    COL6    COL7
john    1        1       1       1       1       1
sally   2        2       2       2       2       2

이 예제는로 인해 단일 열로 중복을 결정 col1합니다 PARTITION BY col1. 여러 열을 포함하려면 간단히 다음에 열을 추가하십시오 PARTITION BY.

ROW_NUMBER()OVER(PARTITION BY Col1, Col2, ... ORDER BY OrderColumn)

2
좋은 답변 감사합니다. 반면에 MSFT는 매우 복잡한 답변을 가지고 있습니다 : stackoverflow.com/questions/18390574/…
Barka

2
@ omachu23 :이 경우 중요하지 않지만 CTE에서 outside ( AND COl1='John') 보다 더 효율적이라고 생각합니다 . 일반적으로 CTE에 필터를 적용해야합니다.
Tim Schmelter

1
@ omachu23 : CTE (주문 제외)의 모든 SQL을 사용할 수 있으므로 Johns로 필터링하려면 ...FROM dbo.Table1 WHERE Col1='John'. 바이올린은 다음과 같습니다. sqlfiddle.com/#!6/fae73/744/0
Tim Schmelter

1
가장 쉬운 해결책은 다음 set rowcount 1 delete from t1 where col1=1 and col2=1같습니다.
Zorgarath

15
이 답변은 col1에 중복 된 행만 삭제합니다. "select"의 열을 "partition by"에 추가하십시오 (예 : 응답에서 select를 사용). RN = ROW_NUMBER () OVER (PARTITION BY col1, col2, col3, col4, col5, col6, col7 ORDER BY col1)
11

158

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)

2
윈도 잉 기능은 훌륭한 솔루션입니다.
Robert Casey

2
나는 약간 혼란 스럽다. 원래 테이블이 아닌 CTE에서 삭제했습니다. 어떻게 작동합니까?
Bigeyes

8
CTE에서 레코드를 삭제하는 @Bigeyes는 실제 실제 테이블에서 해당 레코드를 제거합니다 (CTE에 실제 레코드에 대한 참조가 포함되어 있기 때문에).
Shamseer K

나는 이것이이 게시물까지 사건이었다 몰랐다 ... 감사
으로 zakka 디아즈에게

1
왜 원본과 사본을 모두 삭제 하시겠습니까? 중복을 제거하고 다른 것을 유지하고 싶지 않은 이유를 이해하지 못했습니다.
Rich

52

사용하지 않고 CTE하고 ROW_NUMBER()그냥 단지로 그룹화를 사용하여 레코드를 삭제할 수 있습니다 MAX여기에 함수 예

DELETE
FROM MyDuplicateTable
WHERE ID NOT IN
(
SELECT MAX(ID)
FROM MyDuplicateTable
GROUP BY DuplicateColumn1, DuplicateColumn2, DuplicateColumn3)

4
이 쿼리는 중복되지 않은 레코드를 삭제합니다.
Derek Smalls

8
잘 작동합니다. 감사합니다. @DerekSmalls 이것은 중복되지 않은 레코드를 제거하지 않습니다.
monteirobrena

1
또는 다음을 사용하여 원본 레코드를 유지할 수 있습니다.MIN(ID)
Savage

18
DELETE from search
where id not in (
   select min(id) from search
   group by url
   having count(*)=1

   union

   SELECT min(id) FROM search
   group by url
   having count(*) > 1
)

다시 쓸 수 없습니다 : where id in (select max (id) ... having count (*)> 1)?
Brent

1
나는 가지고 있거나 노동 조합을 사용할 필요가 없다고 생각합니다. 이것으로 충분합니다 : id가없는 곳에서 검색에서 삭제하십시오 (URL로 검색 그룹에서 min (id)를 선택하십시오)
Christopher Yang

9

아래의 삭제 방법도 참조하십시오.

Declare @table table
(col1 varchar(10),col2 int,col3 int, col4 int, col5 int, col6 int, col7 int)
Insert into @table values 
('john',1,1,1,1,1,1),
('john',1,1,1,1,1,1),
('sally',2,2,2,2,2,2),
('sally',2,2,2,2,2,2)

이름이 지정된 샘플 테이블을 작성하고 @table지정된 데이터로로드했습니다.

여기에 이미지 설명을 입력하십시오

Delete  aliasName from (
Select  *,
        ROW_NUMBER() over (Partition by col1,col2,col3,col4,col5,col6,col7 order by col1) as rowNumber
From    @table) aliasName 
Where   rowNumber > 1

Select * from @table

여기에 이미지 설명을 입력하십시오

참고 : Partition by파트의 모든 열을 제공하는 order by경우 큰 의미가 없습니다.

질문은 3 년 전에 요청되었으며 내 답변은 Tim이 게시 한 내용의 다른 버전이지만, 게시하는 것은 누구에게나 도움이됩니다.


9

외래 키와 같은 참조가 없으면 이렇게 할 수 있습니다. 개념 증명을 테스트하고 테스트 데이터가 복제 될 때 많이합니다.

SELECT DISTINCT [col1],[col2],[col3],[col4],[col5],[col6],[col7]

INTO [newTable]

개체 탐색기로 이동하여 이전 테이블을 삭제하십시오.

새 테이블의 이름을 이전 테이블의 이름으로 바꿉니다.


이것이 제가 소개 자료에서 배우고 사용하는 가장 간단한 방법입니다.
eric dec

7

Microsoft는 중복 제거 방법에 대한 정확한 안내서를 제공합니다. 체크 아웃 http://support.microsoft.com/kb/139444를 하십시오.

간단히 말해서 몇 줄만 삭제할 때 중복 항목을 삭제하는 가장 쉬운 방법은 다음과 같습니다.

SET rowcount 1;
DELETE FROM t1 WHERE myprimarykey=1;

myprimarykey 는 행의 식별자입니다.

나는 행 개수를 설정두 개의 행만 복제 를 1로 . 3 개의 행이 복제 된 경우 rowcount 를 2로 설정 하여 처음 두 개를 삭제하고 테이블 t1에 하나만 남겨 둡니다.

그것이 누군가를 돕기를 바랍니다.


1
10k 개의 행이있는 경우 얼마나 많은 행을 복제했는지 어떻게 알 수 있습니까?
Fearghal

@Fearghal 시도 "primaryKey에 의해 myTable 그룹에서 primaryKey 선택, count (*);"
oabarca 2016 년

1
그러나 다양한 수의 중복 행이 있다면 어떨까요? 즉, 행 a에는 2 개의 레코드가 있고 행 b에는 5 개의 레코드가 있고 행 c에는 중복 레코드가 없습니다.
Thermite

1
@ user2070775 모든 행의 하위 집합에만 중복이 있고 일부 중복이 두 번, 일부 또는 네 번 중복되면 어떻게됩니까?
thermite

@ user2070775 "삭제할 행 몇 개"라고 말한 부분을 놓쳤습니다. 또한 SQL의 향후 버전에서 업데이트하거나 삭제 진술에 영향을 미치지 못해 것을 집합 행 개수에 대한 페이지의 경고가있다
테르밋

6

사용하십시오 :

SELECT linkorder
    ,Row_Number() OVER (
        PARTITION BY linkorder ORDER BY linkorder DESC
        ) AS RowNum
FROM u_links

여기에 이미지 설명을 입력하십시오


4

위의 제안 된 솔루션을 시도한 후에 작은 중간 테이블에 작동합니다. 매우 큰 테이블에 대한 솔루션을 제안 할 수 있습니다. 반복적으로 실행되기 때문입니다.

  1. 의 모든 의존성 뷰를 삭제 LargeSourceTable
  2. sql managment studio를 사용하여 의존성을 찾을 수 있습니다. 테이블을 마우스 오른쪽 버튼으로 클릭하고 "종속성보기"를 클릭하십시오.
  3. 테이블 이름을 바꾸십시오.
  4. sp_rename 'LargeSourceTable', 'LargeSourceTable_Temp'; GO
  5. LargeSourceTable다시 작성 하지만 이제 중복을 정의하는 모든 열이있는 기본 키를 추가하십시오.WITH (IGNORE_DUP_KEY = ON)
  6. 예를 들면 다음과 같습니다.

    CREATE TABLE [dbo].[LargeSourceTable] ( ID int IDENTITY(1,1), [CreateDate] DATETIME CONSTRAINT [DF_LargeSourceTable_CreateDate] DEFAULT (getdate()) NOT NULL, [Column1] CHAR (36) NOT NULL, [Column2] NVARCHAR (100) NOT NULL, [Column3] CHAR (36) NOT NULL, PRIMARY KEY (Column1, Column2) WITH (IGNORE_DUP_KEY = ON) ); GO

  7. 새로 작성된 테이블의 첫 번째 장소에서 삭제 한보기를 다시 작성하십시오.

  8. 이제 다음 SQL 스크립트를 실행하면 페이지 당 1,000,000 개의 행으로 결과가 표시되고 페이지 당 행 번호를 변경하여 결과를 더 자주 볼 수 있습니다.

  9. 참고, 내가 설정하는 것이 IDENTITY_INSERT에 하나 때문에 열이 떨어져 나가 또한 복사있어 자동 증분 ID를 포함

SET IDENTITY_INSERT LargeSourceTable ON DECLARE @PageNumber AS INT, @RowspPage AS INT DECLARE @TotalRows AS INT declare @dt varchar(19) SET @PageNumber = 0 SET @RowspPage = 1000000 select @TotalRows = count (*) from LargeSourceTable_TEMP

While ((@PageNumber - 1) * @RowspPage < @TotalRows )
Begin
    begin transaction tran_inner
        ; with cte as
        (
            SELECT * FROM LargeSourceTable_TEMP ORDER BY ID
            OFFSET ((@PageNumber) * @RowspPage) ROWS
            FETCH NEXT @RowspPage ROWS ONLY
        )

        INSERT INTO LargeSourceTable 
        (
             ID                     
            ,[CreateDate]       
            ,[Column1]   
            ,[Column2] 
            ,[Column3]       
        )       
        select 
             ID                     
            ,[CreateDate]       
            ,[Column1]   
            ,[Column2] 
            ,[Column3]       
        from cte

    commit transaction tran_inner

    PRINT 'Page: ' + convert(varchar(10), @PageNumber)
    PRINT 'Transfered: ' + convert(varchar(20), @PageNumber * @RowspPage)
    PRINT 'Of: ' + convert(varchar(20), @TotalRows)

    SELECT @dt = convert(varchar(19), getdate(), 121)
    RAISERROR('Inserted on: %s', 0, 1, @dt) WITH NOWAIT
    SET @PageNumber = @PageNumber + 1
End

SET IDENTITY_INSERT LargeSourceTable OFF


4

두 가지 해결책이 있습니다 mysql.

A)DELETE JOIN 문을 사용하여 중복 행 삭제

DELETE t1 FROM contacts t1
INNER JOIN contacts t2 
WHERE 
    t1.id < t2.id AND 
    t1.email = t2.email;

이 쿼리는 연락처 테이블을 두 번 참조하므로 테이블 별명 t1t2 .

출력은 다음과 같습니다.

쿼리 1 개, 영향을받는 4 개의 행 (0.10 초)

중복 행을 삭제하고를 유지하려는 경우 lowest id다음 명령문을 사용할 수 있습니다.

DELETE c1 FROM contacts c1
INNER JOIN contacts c2 
WHERE
    c1.id > c2.id AND 
    c1.email = c2.email;

   

B) 중간 테이블을 사용하여 중복 행 삭제

다음은 중간 테이블을 사용하여 중복 행을 제거하는 단계를 보여줍니다.

    1. 중복 행을 삭제하려는 원래 테이블과 동일한 구조로 새 테이블을 작성하십시오.

    2. 원래 테이블에서 즉시 테이블로 고유 한 행을 삽입하십시오.

    3. 원래 테이블에서 즉시 테이블로 별개의 행을 삽입하십시오.

 

단계 1. 구조가 원래 테이블과 동일한 새 테이블을 작성하십시오.

CREATE TABLE source_copy LIKE source;

단계 2. 원래 테이블에서 새 테이블로 고유 한 행을 삽입하십시오.

INSERT INTO source_copy
SELECT * FROM source
GROUP BY col; -- column that has duplicate values

3 단계. 원래 테이블을 삭제하고 즉시 테이블 이름을 원래 테이블로 바꿉니다.

DROP TABLE source;
ALTER TABLE source_copy RENAME TO source;

출처 : http://www.mysqltutorial.org/mysql-delete-duplicate-rows/


2
-- this query will keep only one instance of a duplicate record.
;WITH cte
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY col1, col2, col3-- based on what? --can be multiple columns
                                       ORDER BY ( SELECT 0)) RN
         FROM   Mytable)



delete  FROM cte
WHERE  RN > 1

2

필드에 따라 중복 레코드별로 그룹화 한 다음 레코드 중 하나를 보유하고 나머지를 삭제해야합니다. 예를 들면 다음과 같습니다.

DELETE prg.Person WHERE Id IN (
SELECT dublicateRow.Id FROM
(
select MIN(Id) MinId, NationalCode
 from  prg.Person group by NationalCode  having count(NationalCode ) > 1
 ) GroupSelect
 JOIN  prg.Person dublicateRow ON dublicateRow.NationalCode = GroupSelect.NationalCode 
 WHERE dublicateRow.Id <> GroupSelect.MinId)

2

거대한 (수백만 개의 레코드) 테이블에서 중복을 삭제하는 데 시간이 오래 걸릴 수 있습니다. 삭제하지 않고 선택한 행의 임시 테이블에 대량 삽입을 수행하는 것이 좋습니다.

--REWRITING YOUR CODE(TAKE NOTE OF THE 3RD LINE) WITH CTE AS(SELECT NAME,ROW_NUMBER() 
OVER (PARTITION BY NAME ORDER BY NAME) ID FROM @TB) SELECT * INTO #unique_records FROM 
CTE WHERE ID =1;

2

SQL Server에서 가장 간단한 방법은 여러 가지 방법으로 수행 할 수 있습니다. 중복 행 테이블에서 새 임시 테이블로 구별 행을 삽입하십시오. 그런 다음 중복 행 테이블에서 모든 데이터를 삭제 한 다음 아래와 같이 중복이없는 임시 테이블에서 모든 데이터를 삽입하십시오.

select distinct * into #tmp From table
   delete from table
   insert into table
   select * from #tmp drop table #tmp

   select * from table

공통 테이블 표현식 (CTE)을 사용하여 중복 행 삭제

With CTE_Duplicates as 
(select id,name , row_number() 
over(partition by id,name order by id,name ) rownumber  from table  ) 
delete from CTE_Duplicates where rownumber!=1

1
with myCTE
as

(
select productName,ROW_NUMBER() over(PARTITION BY productName order by slno) as Duplicate from productDetails
)
Delete from myCTE where Duplicate>1

1

https://support.microsoft.com/en-us/help/139444/how-to-remove-duplicate-rows-from-a-table-in-sql-server를 참조하십시오.

중복을 제거한다는 아이디어는

  • a) 중복되지 않은 행 보호
  • b) 중복으로 함께 규정 된 많은 행 중 하나를 유지하십시오.

단계별

  • 1) 먼저 중복 정의를 만족시키는 행을 식별하고 임시 테이블에 삽입하십시오 (예 : #tableAll).
  • 2) #tableUnique와 같이 임시 테이블에 중복되지 않는 (단일 행) 또는 구별 행을 선택하십시오.
  • 3) 소스 테이블에서 #tableAll을 조인하여 삭제하여 복제본을 삭제합니다.
  • 4) #tableUnique의 모든 행을 소스 테이블에 삽입하십시오.
  • 5) #tableAll 및 #tableUnique 삭제

1

테이블에 열을 임시로 추가 할 수 있다면 이것이 나를 위해 일한 솔루션이었습니다.

ALTER TABLE dbo.DUPPEDTABLE ADD RowID INT NOT NULL IDENTITY(1,1)

그런 다음 MIN과 GROUP BY의 조합을 사용하여 삭제를 수행하십시오.

DELETE b
FROM dbo.DUPPEDTABLE b
WHERE b.RowID NOT IN (
                     SELECT MIN(RowID) AS RowID
                     FROM dbo.DUPPEDTABLE a WITH (NOLOCK)
                     GROUP BY a.ITEM_NUMBER,
                              a.CHARACTERISTIC,
                              a.INTVALUE,
                              a.FLOATVALUE,
                              a.STRINGVALUE
                 );

삭제가 올바르게 수행되었는지 확인하십시오.

SELECT a.ITEM_NUMBER,
    a.CHARACTERISTIC,
    a.INTVALUE,
    a.FLOATVALUE,
    a.STRINGVALUE, COUNT(*)--MIN(RowID) AS RowID
FROM dbo.DUPPEDTABLE a WITH (NOLOCK)
GROUP BY a.ITEM_NUMBER,
    a.CHARACTERISTIC,
    a.INTVALUE,
    a.FLOATVALUE,
    a.STRINGVALUE
ORDER BY COUNT(*) DESC 

결과는 1보다 큰 개수의 행이 없어야합니다. 마지막으로 rowid 열을 제거하십시오.

ALTER TABLE dbo.DUPPEDTABLE DROP COLUMN RowID;

0

한 단계에서 정보를 잃지 않고 복제 된 행을 제거하는 다른 방법은 다음과 같습니다.

delete from dublicated_table t1 (nolock)
join (
    select t2.dublicated_field
    , min(len(t2.field_kept)) as min_field_kept
    from dublicated_table t2 (nolock)
    group by t2.dublicated_field having COUNT(*)>1
) t3 
on t1.dublicated_field=t3.dublicated_field 
    and len(t1.field_kept)=t3.min_field_kept

0

오 와우, 나는이 모든 답변을 준비함으로써 너무 어리석은 느낌을 느낍니다. 모든 CTE 및 임시 테이블 등의 전문가 답변과 같습니다.

그리고 내가 작동하게하기 위해 MAX를 사용하여 ID 열을 집계했습니다.

DELETE FROM table WHERE col1 IN (
    SELECT MAX(id) FROM table GROUP BY id HAVING ( COUNT(col1) > 1 )
)

참고 : 한 번에 하나의 중복 행 집합 만 삭제되므로 중복을 제거하려면 여러 번 실행해야 할 수도 있습니다.


원본을 남기지 않고 모든 복제본을 제거하므로 작동하지 않습니다. OP는 원본 레코드를 보존하도록 요청하고 있습니다.
0xdd

2
사실이 아닌 경우 max는 조건을 만족하는 최대 ID를 제공합니다. 이것이 사실이 아닌 경우, 다운 투표에 대한 귀하의 사례를 증명하십시오.
엉망 업

0
DECLARE @TB TABLE(NAME VARCHAR(100));
INSERT INTO @TB VALUES ('Red'),('Red'),('Green'),('Blue'),('White'),('White')
--**Delete by Rank**
;WITH CTE AS(SELECT NAME,DENSE_RANK() OVER (PARTITION BY NAME ORDER BY NEWID()) ID FROM @TB)
DELETE FROM CTE WHERE ID>1
SELECT NAME FROM @TB;
--**Delete by Row Number** 
;WITH CTE AS(SELECT NAME,ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY NAME) ID FROM @TB)
DELETE FROM CTE WHERE ID>1;
SELECT NAME FROM @TB;

거대한 (수백만 개의 레코드) 테이블에서 중복을 삭제하는 데 시간이 오래 걸릴 수 있습니다. 선택한 행의 임시 테이블에 대량 삽입하는 대신 삭제하는 것이 좋습니다. '--CTE AS (SELECT NAME, ROW_NUMBER () OVER (이름 별 주문 또는 이름 별 ID) ID @ @TB)를 사용하여 코드를 다시 작성 (3 번째 줄 참고) SELECT * INTE #CTE WHERE ID = 1; '
Emmanuel Bull

0
DELETE FROM TBL1  WHERE ID  IN
(SELECT ID FROM TBL1  a WHERE ID!=
(select MAX(ID) from TBL1  where DUPVAL=a.DUPVAL 
group by DUPVAL
having count(DUPVAL)>1))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.