Làm cách nào để xóa các hàng trùng lặp trong SQL Server?


415

Làm thế nào tôi có thể xóa các hàng trùng lặp , nơi không unique row idtồn tại?

Bàn của tôi là

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

Tôi muốn để lại những điều sau đây sau khi loại bỏ trùng lặp:

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

Tôi đã thử một vài truy vấn nhưng tôi nghĩ chúng phụ thuộc vào việc có id hàng vì tôi không nhận được kết quả mong muốn. Ví dụ:

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

5
Đây không phải là bản sao của liên kết đầu tiên. Trong câu hỏi này không có ID hàng và trong câu hỏi được liên kết có ID hàng. Rất khác nhau.
Công nghệ ngoài hành tinh

thay đổi 'CHỌN id TỪ bảng NHÓM THEO id HAVING' để có chức năng tổng hợp, ví dụ MAX / MIN và nó sẽ hoạt động.
lộn xộn

Câu trả lời:


785

Tôi thích CTE và ROW_NUMBERvì cả hai kết hợp cho phép chúng tôi xem những hàng nào bị xóa (hoặc cập nhật), do đó chỉ cần thay đổi DELETE FROM CTE...thành 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 (kết quả là khác nhau; tôi cho rằng đó là do lỗi đánh máy từ phía bạn)

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

Ví dụ này xác định trùng lặp bởi một cột duy nhất col1PARTITION BY col1. Nếu bạn muốn bao gồm nhiều cột, chỉ cần thêm chúng vào PARTITION BY:

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

2
Cảm ơn bạn cho một câu trả lời tuyệt vời. Ngược lại, MSFT có một câu trả lời rất phức tạp ở đây: stackoverflow.com/questions/18390574/ory
Barka

2
@ omachu23: trong trường hợp này không thành vấn đề, mặc dù tôi nghĩ rằng CTE hiệu quả hơn bên ngoài ( AND COl1='John'). Thông thường bạn nên áp dụng bộ lọc trong CTE.
Tim Schmelter

1
@ omachu23: bạn có thể sử dụng bất kỳ SQL nào trong CTE (ngoài việc đặt hàng), vì vậy nếu bạn muốn lọc theo Johns : ...FROM dbo.Table1 WHERE Col1='John'. Đây là câu đố: sqlfiddle.com/#!6/fae73/744/0
Tim Schmelter

1
Giải pháp đơn giản nhất có thể được set rowcount 1 delete from t1 where col1=1 and col2=1thấy ở đây
Zorgarath

15
Câu trả lời này sẽ chỉ xóa các hàng có trùng lặp trong col1. Thêm các cột trong "chọn" vào "phân vùng theo", ví dụ: sử dụng chọn trong câu trả lời: RN = ROW_NUMBER () QUÁ (PHẦN THAM GIA BỞI col1, col2, col3, col4, col5, col6, col7 ĐẶT HÀNG theo col1)
rlee

158

Tôi muốn CTE xóa các hàng trùng lặp khỏi bảng máy chủ sql

đặc biệt khuyên bạn nên theo dõi bài viết này :: http://codaffection.com/sql-server-article/delete-d repeatate -rows-in-sql-server /

bằng cách giữ bản gốc

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

mà không giữ bản gốc

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
Chức năng cửa sổ là một giải pháp tuyệt vời.
Robert Casey

2
Tôi hơi bối rối. Bạn đã xóa nó khỏi CTE chứ không phải bảng gốc. Vì vậy, làm thế nào nó hoạt động?
Bigeyes

8
@Bigeyes xóa các bản ghi khỏi CTE sẽ xóa các bản ghi tương ứng khỏi bảng vật lý thực tế (vì CTE chứa tham chiếu đến các bản ghi thực tế).
Shamseer K

Tôi không biết đây là trường hợp nào cho đến khi đăng bài này ... Cảm ơn bạn
Zakk Diaz

1
Tại sao bạn muốn xóa cả bản gốc và bản sao của nó? Tôi không hiểu tại sao bạn không muốn xóa bản sao và giữ bản sao khác.
Giàu

52

Không sử dụng CTEROW_NUMBER()bạn chỉ có thể xóa các bản ghi chỉ bằng cách sử dụng nhóm bằng MAXchức năng ở đây và ví dụ

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

4
Truy vấn này sẽ xóa các bản ghi không trùng lặp.
Derek Smalls

8
Điều này hoạt động tốt, cảm ơn bạn. @DerekSmalls này không loại bỏ các hồ sơ không trùng lặp của tôi.
monteirobrena

1
Hoặc bạn có thể giữ các bản ghi gốc bằng cách sử dụngMIN(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
)

Bạn không thể viết lại vào: id trong (chọn max (id) ... có số đếm (*)> 1)?
Brent

1
Tôi không tin rằng có bất kỳ nhu cầu sử dụng có hoặc kết hợp nào, điều này sẽ đủ: xóa khỏi tìm kiếm nơi id không có trong (chọn min (id) khỏi nhóm tìm kiếm theo url)
Christopher Yang

9

Xin vui lòng xem cách xóa dưới đây quá.

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)

Tạo một bảng mẫu có tên @tablevà tải nó với dữ liệu đã cho.

nhập mô tả hình ảnh ở đây

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

nhập mô tả hình ảnh ở đây

Lưu ý: Nếu bạn đưa ra tất cả các cột trong Partition byphần, thì order bykhông có nhiều ý nghĩa.

Tôi biết, câu hỏi được hỏi ba năm trước, và câu trả lời của tôi là một phiên bản khác của những gì Tim đã đăng, nhưng việc đăng lên chỉ cần nó hữu ích cho bất cứ ai.


9

Nếu bạn không có tài liệu tham khảo, như khóa ngoại, bạn có thể làm điều này. Tôi làm điều đó rất nhiều khi kiểm tra bằng chứng về khái niệm và dữ liệu kiểm tra bị trùng lặp.

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

INTO [newTable]

Đi vào trình thám hiểm đối tượng và xóa bảng cũ.

Đổi tên bảng mới với tên của bảng cũ.


Đây là cách đơn giản nhất mà tôi học được trong các tài liệu giới thiệu của mình và tôi sử dụng.
eric

7

Microsoft có một hướng dẫn gọn gàng vey ry về cách loại bỏ trùng lặp. Hãy xem http://support.microsoft.com/kb/139444

Tóm lại, đây là cách dễ nhất để xóa các bản sao khi bạn chỉ có một vài hàng để xóa:

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

myprimarykey là định danh cho hàng.

Tôi đặt hàng đếm thành 1 vì tôi chỉ có hai hàng được nhân đôi. Nếu tôi có 3 hàng trùng lặp thì tôi sẽ đặt hàng đếm thành 2 để nó xóa hai hàng đầu tiên mà nó nhìn thấy và chỉ để lại một hàng trong bảng t1.

Hy vọng nó sẽ giúp được ai


1
Làm thế nào để tôi biết có bao nhiêu hàng tôi đã nhân đôi nếu tôi có 10k hàng?
Fearghal

@Fearghal thử "chọn chínhKey, đếm (*) từ nhóm myTable theo chínhKey;"
oabarca

1
Nhưng nếu có số lượng hàng trùng lặp khác nhau thì sao? tức là hàng a có 2 bản ghi và hàng b có 5 bản ghi và hàng c không có bản ghi trùng lặp
thermite

1
@ user2070775 Điều gì sẽ xảy ra nếu chỉ một tập hợp con của tất cả các hàng có trùng lặp và trong số các bản sao đó, một số được nhân đôi hai lần và một số ba hoặc bốn lần?
thermite

@ user2070775 Tôi đã bỏ lỡ phần mà bạn nói "chỉ một vài hàng để xóa". Ngoài ra, có một cảnh báo trên trang về thiết lập số lượng hàng trong các phiên bản tương lai của sql, nó sẽ không ảnh hưởng đến cập nhật hoặc xóa các câu lệnh
thermite

6

Thử sử dụng:

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

nhập mô tả hình ảnh ở đây


4

Sau khi thử giải pháp được đề xuất ở trên, nó hoạt động cho các bảng vừa nhỏ. Tôi có thể đề xuất giải pháp đó cho các bảng rất lớn. kể từ khi nó chạy trong các lần lặp.

  1. Bỏ tất cả các quan điểm phụ thuộc của LargeSourceTable
  2. bạn có thể tìm thấy các phụ thuộc bằng cách sử dụng studio quản lý sql, nhấp chuột phải vào bảng và nhấp vào "Xem phụ thuộc"
  3. Đổi tên bảng:
  4. sp_rename 'LargeSourceTable', 'LargeSourceTable_Temp'; GO
  5. Tạo LargeSourceTablelại, nhưng bây giờ, thêm khóa chính với tất cả các cột xác định trùng lặp thêmWITH (IGNORE_DUP_KEY = ON)
  6. Ví dụ:

    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. Tạo lại các chế độ xem mà bạn đã bỏ ở vị trí đầu tiên cho bảng đã tạo mới

  8. Bây giờ, Chạy tập lệnh sql sau, bạn sẽ thấy kết quả trong 1.000.000 hàng trên mỗi trang, bạn có thể thay đổi số hàng trên mỗi trang để xem kết quả thường xuyên hơn.

  9. Lưu ý rằng tôi đã IDENTITY_INSERTbật và tắt vì một cột chứa id tự động tăng, tôi cũng đang sao chép

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

Có hai giải pháp trong mysql:

A) Xóa các hàng trùng lặp bằng cách sử dụng DELETE JOINcâu lệnh

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

Truy vấn này tham chiếu bảng liên hệ hai lần, do đó, nó sử dụng bí danh bảng t1t2 .

Đầu ra là:

1 Truy vấn OK, 4 hàng bị ảnh hưởng (0,10 giây)

Trong trường hợp bạn muốn xóa các hàng trùng lặp và giữ nguyên lowest id, bạn có thể sử dụng câu lệnh sau:

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

   

B) Xóa các hàng trùng lặp bằng bảng trung gian

Sau đây cho thấy các bước để loại bỏ các hàng trùng lặp bằng bảng trung gian:

    1. Tạo một bảng mới với cấu trúc giống như bảng gốc mà bạn muốn xóa các hàng trùng lặp.

    2. Chèn các hàng riêng biệt từ bảng gốc vào bảng ngay lập tức.

    3. Chèn các hàng riêng biệt từ bảng gốc vào bảng ngay lập tức.

 

Bước 1. Tạo một bảng mới có cấu trúc giống như bảng gốc:

CREATE TABLE source_copy LIKE source;

Bước 2. Chèn các hàng riêng biệt từ bảng gốc vào bảng mới:

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

Bước 3. thả bảng gốc và đổi tên bảng ngay lập tức thành bảng ban đầu

DROP TABLE source;
ALTER TABLE source_copy RENAME TO source;

Nguồn: http://www.mysqltutorial.org/mysql-delete-dsplate-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

Bạn cần nhóm theo các bản ghi trùng lặp theo (các) trường, sau đó giữ một trong các bản ghi và xóa phần còn lại. Ví dụ:

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

Xóa các bản sao khỏi một bảng lớn (vài triệu bản ghi) có thể mất nhiều thời gian. Tôi đề nghị bạn thực hiện chèn số lượng lớn vào bảng tạm thời của các hàng đã chọn thay vì xóa.

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

Nó có thể được thực hiện bằng nhiều cách trong máy chủ sql cách đơn giản nhất để làm như vậy là: Chèn các hàng riêng biệt từ bảng hàng trùng lặp vào bảng tạm thời mới. Sau đó xóa tất cả dữ liệu khỏi bảng hàng trùng lặp, sau đó chèn tất cả dữ liệu từ bảng tạm thời không có trùng lặp như hiển thị bên dưới.

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

   select * from table

Xóa các hàng trùng lặp bằng Biểu thức bảng chung (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

Với tham chiếu đến https://support.microsoft.com/en-us/help/139444/how-to-remove-d repeatate -rows-from-a-test-in-sql-server

Ý tưởng loại bỏ trùng lặp liên quan

  • a) Bảo vệ những hàng không trùng lặp
  • b) Giữ lại một trong nhiều hàng đủ điều kiện trùng lặp.

Từng bước một

  • 1) Trước tiên, xác định các hàng thỏa mãn định nghĩa trùng lặp và chèn chúng vào bảng tạm thời, giả sử #table ALL.
  • 2) Chọn các hàng không trùng lặp (một hàng) hoặc các hàng riêng biệt vào bảng tạm thời nói #tableUnique.
  • 3) Xóa khỏi bảng nguồn tham gia #table ALL để xóa các bản sao.
  • 4) Chèn vào bảng nguồn tất cả các hàng từ #tableUnique.
  • 5) Thả #table ALL và #tableUnique

1

Nếu bạn có khả năng tạm thời thêm một cột vào bảng, đây là một giải pháp hiệu quả với tôi:

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

Sau đó thực hiện XÓA bằng cách sử dụng kết hợp MIN và NHÓM THEO

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

Xác minh rằng XÓA thực hiện chính xác:

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 

Kết quả sẽ không có hàng nào có số đếm lớn hơn 1. Cuối cùng, hãy xóa cột rowid:

ALTER TABLE dbo.DUPPEDTABLE DROP COLUMN RowID;

0

Một cách khác để loại bỏ các hàng được công bố mà không mất thông tin trong một bước như sau:

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

Ôi chà, tôi cảm thấy thật ngu ngốc khi sẵn sàng tất cả các câu trả lời này, chúng giống như câu trả lời của các chuyên gia với tất cả CTE và bảng tạm thời, v.v.

Và tất cả những gì tôi đã làm để làm cho nó hoạt động chỉ đơn giản là tổng hợp cột ID bằng cách sử dụng MAX.

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

LƯU Ý: bạn có thể cần chạy nhiều lần để xóa trùng lặp vì điều này sẽ chỉ xóa một bộ hàng trùng lặp tại một thời điểm.


Điều này sẽ không hoạt động vì nó sẽ loại bỏ tất cả các bản sao mà không để lại bản gốc. OP đang yêu cầu giữ gìn hồ sơ gốc.
0xdd

2
Không đúng, max sẽ cung cấp cho bạn ID tối đa thỏa mãn điều kiện. Nếu điều đó không đúng, hãy chứng minh trường hợp của bạn để bỏ phiếu.
vào

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;

Xóa các bản sao khỏi một bảng lớn (vài triệu bản ghi) có thể mất nhiều thời gian. Tôi đề nghị bạn thực hiện chèn số lượng lớn vào bảng tạm thời của các hàng đã chọn thay vì xóa. '- TÌM HIỂU MÃ SỐ CỦA BẠN (HÃY LƯU Ý DÒNG 3RD) VỚI CTE NHƯ (CHỌN TÊN, ROW_NUMBER () QUÁ (THAM GIA BỞI TÊN ĐẶT HÀNG THEO TÊN) ID TỪ @TB) CHỌN * VÀO #unique_records TỪ 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))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.