Bagaimana cara menghapus duplikat baris di SQL Server?

415

Bagaimana saya bisa menghapus baris duplikat di mana tidak unique row idada?

Meja saya

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

Saya ingin diberi yang berikut setelah penghapusan duplikat:

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

Saya sudah mencoba beberapa pertanyaan tetapi saya pikir mereka bergantung pada memiliki id baris karena saya tidak mendapatkan hasil yang diinginkan. Sebagai contoh:

DELETE
FROM table
WHERE col1 IN (
    SELECT id
    FROM table
    GROUP BY id
    HAVING (COUNT(col1) > 1)
)
Fearghal
sumber
5
Ini bukan duplikat dari tautan pertama. Dalam pertanyaan ini tidak ada ID baris dan dalam pertanyaan terkait ada ID baris. Sangat berbeda.
Teknologi Alien
ubah 'SELECT id FROM dari tabel GROUP BY id HAVING' untuk memiliki fungsi teragregasi misalnya MAX / MIN dan itu harus bekerja.
kacau

Jawaban:

785

Saya suka CTE dan ROW_NUMBERkarena keduanya digabungkan memungkinkan kami untuk melihat baris mana yang dihapus (atau diperbarui), oleh karena itu ubah saja DELETE FROM CTE...ke 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 (hasilnya berbeda; saya berasumsi bahwa itu karena kesalahan ketik di pihak Anda)

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

Contoh ini menentukan duplikat oleh satu kolom col1karena PARTITION BY col1. Jika Anda ingin memasukkan beberapa kolom, cukup tambahkan ke PARTITION BY:

ROW_NUMBER()OVER(PARTITION BY Col1, Col2, ... ORDER BY OrderColumn)
Tim Schmelter
sumber
2
Terima kasih atas jawaban yang bagus. Sebaliknya MSFT memiliki jawaban yang sangat rumit di sini: stackoverflow.com/questions/18390574/…
Barka
2
@ omachu23: dalam hal ini tidak masalah, meskipun saya pikir itu lebih efisien di CTE daripada di luar ( AND COl1='John'). Biasanya Anda harus menerapkan filter di CTE.
Tim Schmelter
1
@ omachu23: Anda dapat menggunakan SQL apapun dalam CTE (terlepas dari pemesanan), jadi jika Anda ingin filter oleh Johns: ...FROM dbo.Table1 WHERE Col1='John'. Ini biola: sqlfiddle.com/#!6/fae73/744/0
Tim
1
Solusi termudah mungkin set rowcount 1 delete from t1 where col1=1 and col2=1seperti yang terlihat di sini
Zorgarath
15
Jawaban ini hanya akan menghapus baris yang memiliki duplikat di col1. Tambahkan kolom di "pilih" ke "partisi dengan", misalnya menggunakan pilih dalam jawaban: RN = ROW_NUMBER () LEBIH DARI (PARTISI DENGAN OLEH col1, col2, col3, col4, col5, col6, col7 ORDER BY col1)
lari
159

Saya lebih suka CTE untuk menghapus duplikat baris dari tabel sql server

sangat menyarankan untuk mengikuti artikel ini :: http://codaffection.com/sql-server-article/delete-duplicate-rows-in-sql-server/

dengan menjaga yang asli

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

tanpa menyimpan yang asli

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)
Shamseer K
sumber
2
Fungsi windowing adalah solusi yang bagus.
Robert Casey
2
Saya agak bingung. Anda menghapusnya dari CTE bukan tabel asli. Jadi bagaimana cara kerjanya?
Bigeyes
8
@Bigeyes menghapus catatan dari CTE akan menghapus catatan yang sesuai dari tabel fisik yang sebenarnya (karena CTE berisi referensi ke catatan aktual).
Shamseer K
Saya tidak tahu ini adalah masalahnya sampai posting ini ... Terima kasih
Zakk Diaz
1
Mengapa Anda ingin menghapus yang asli dan duplikatnya? Saya tidak mengerti mengapa Anda tidak ingin hanya menghapus duplikat dan menyimpan yang lain.
Kaya
52

Tanpa menggunakan CTEdan ROW_NUMBER()Anda hanya dapat menghapus catatan hanya dengan menggunakan grup dengan MAXfungsi di sini adalah dan contoh

DELETE
FROM MyDuplicateTable
WHERE ID NOT IN
(
SELECT MAX(ID)
FROM MyDuplicateTable
GROUP BY DuplicateColumn1, DuplicateColumn2, DuplicateColumn3)
Aamir
sumber
4
Kueri ini akan menghapus catatan non-duplikat.
Derek Smalls
8
Ini berfungsi baik, terima kasih. @DerekSmalls ini tidak menghapus catatan non-duplikat saya.
monteirobrena
1
Atau Anda dapat menyimpan catatan asli menggunakanMIN(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
)
Shoja Hamid
sumber
Tidak bisakah Anda menulis ulang ke: di mana id dalam (pilih maks (id) ... setelah dihitung (*)> 1)?
Brent
1
Saya tidak percaya ada kebutuhan untuk menggunakan memiliki atau gabungan, ini sudah cukup: hapus dari pencarian di mana id tidak masuk (pilih min (id) dari grup pencarian dengan url)
Christopher Yang
9

Silakan lihat cara penghapusan di bawah ini juga.

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)

Membuat tabel sampel bernama @tabledan memuatnya dengan data yang diberikan.

masukkan deskripsi gambar di sini

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

masukkan deskripsi gambar di sini

Catatan: Jika Anda memberikan semua kolom di Partition bybagian tersebut, maka order bytidak memiliki banyak arti.

Saya tahu, pertanyaan itu ditanyakan tiga tahun lalu, dan jawaban saya adalah versi lain dari apa yang telah diposting Tim, Tetapi memposting hanya jika itu membantu bagi siapa pun.

Jithin Shaji
sumber
9

Jika Anda tidak memiliki referensi, seperti kunci asing, Anda dapat melakukan ini. Saya sering melakukannya ketika menguji bukti konsep dan data uji diduplikasi.

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

INTO [newTable]

Pergilah ke objek explorer dan hapus tabel lama.

Ganti nama tabel baru dengan nama tabel lama.

Rhys
sumber
Ini adalah cara paling sederhana yang saya pelajari dalam materi intro saya, dan yang saya gunakan.
eric
7

Microsoft memiliki panduan yang sangat rapi tentang cara menghapus duplikat. Lihat http://support.microsoft.com/kb/139444

Secara singkat, berikut adalah cara termudah untuk menghapus duplikat ketika Anda hanya memiliki beberapa baris untuk dihapus:

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

myprimarykey adalah pengidentifikasi untuk baris.

Saya menetapkan rowcount ke 1 karena saya hanya memiliki dua baris yang digandakan. Jika saya memiliki 3 baris digandakan maka saya akan menetapkan rowcount ke 2 sehingga menghapus dua yang pertama yang dilihatnya dan hanya menyisakan satu di tabel t1.

Semoga ini bisa membantu siapa saja

oabarca
sumber
1
Bagaimana saya tahu berapa banyak baris yang telah saya duplikasi jika saya memiliki baris 10k?
Fearghal
@Fearghal coba "pilih primaryKey, hitung (*) dari grup myTable oleh primaryKey;"
oabarca
1
Tetapi bagaimana jika ada jumlah baris duplikat yang berbeda? yaitu baris a memiliki 2 catatan dan baris b memiliki 5 catatan dan baris c tidak memiliki catatan duplikat
termit
1
@ user2070775 Bagaimana jika hanya sebagian dari semua baris yang memiliki duplikat, dan dari mereka yang duplikat, ada yang digandakan dua kali dan tiga atau empat kali?
thermite
@ user2070775 Saya melewatkan bagian di mana Anda mengatakan "hanya beberapa baris untuk dihapus". Juga ada peringatan di halaman tentang set rowcount yang di masa depan versi sql wont mempengaruhi update atau delete pernyataan
termit
6

Coba Gunakan:

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

masukkan deskripsi gambar di sini

Fezal halai
sumber
4

Setelah mencoba solusi yang disarankan di atas, itu berfungsi untuk tabel menengah kecil. Saya dapat menyarankan solusi untuk tabel yang sangat besar. karena berjalan di iterasi.

  1. Jatuhkan semua tampilan ketergantungan pada LargeSourceTable
  2. Anda dapat menemukan ketergantungan dengan menggunakan studio manajemen sql, klik kanan pada tabel dan klik "Lihat Dependensi"
  3. Ganti nama tabel:
  4. sp_rename 'LargeSourceTable', 'LargeSourceTable_Temp'; GO
  5. Buat LargeSourceTablelagi, tapi sekarang, tambahkan kunci utama dengan semua kolom yang menentukan penambahan duplikasiWITH (IGNORE_DUP_KEY = ON)
  6. Sebagai contoh:

    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. Buat lagi tampilan yang Anda jatuhkan di tempat pertama untuk tabel yang baru dibuat

  8. Sekarang, Jalankan skrip sql berikut, Anda akan melihat hasilnya dalam 1.000.000 baris per halaman, Anda dapat mengubah nomor baris per halaman untuk melihat hasilnya lebih sering.

  9. Perhatikan, bahwa saya mengatur IDENTITY_INSERTon dan off karena satu kolom berisi id tambahan otomatis, yang juga saya salin

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

Moshe Taieb
sumber
4

Ada dua solusi di mysql:

A) Hapus duplikat baris menggunakan DELETE JOINpernyataan

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

Kueri ini merujuk tabel kontak dua kali, oleh karena itu, ia menggunakan alias tabel t1dan t2.

Outputnya adalah:

1 Kueri OK, 4 baris terpengaruh (0,10 dtk)

Jika Anda ingin menghapus duplikat baris dan menyimpannya lowest id, Anda dapat menggunakan pernyataan berikut:

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

   

B) Hapus baris duplikat menggunakan tabel perantara

Berikut ini menunjukkan langkah-langkah untuk menghapus baris duplikat menggunakan tabel perantara:

    1. Buat tabel baru dengan struktur yang sama dengan tabel asli yang ingin Anda hapus baris duplikat.

    2. Masukkan baris berbeda dari tabel asli ke tabel langsung.

    3. Masukkan baris berbeda dari tabel asli ke tabel langsung.

 

Langkah 1. Buat tabel baru yang strukturnya sama dengan tabel asli:

CREATE TABLE source_copy LIKE source;

Langkah 2. Masukkan baris berbeda dari tabel asli ke tabel baru:

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

Langkah 3. jatuhkan tabel asli dan ganti nama tabel langsung ke yang asli

DROP TABLE source;
ALTER TABLE source_copy RENAME TO source;

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

Bashirpour
sumber
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
Hasan Shouman
sumber
2

Anda perlu mengelompokkan berdasarkan catatan duplikat menurut bidang, lalu tahan salah satu catatan dan hapus sisanya. Sebagai contoh:

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)
Hadi Salehy
sumber
2

Menghapus duplikat dari tabel besar (beberapa juta catatan) mungkin memakan waktu lama. Saya menyarankan Anda melakukan memasukkan massal ke tabel temp dari baris yang dipilih daripada menghapus.

--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;
Emmanuel Bull
sumber
2

Ini dapat dilakukan dengan banyak cara di sql server cara paling sederhana untuk melakukannya adalah: Masukkan baris yang berbeda dari tabel baris duplikat ke tabel sementara baru. Kemudian hapus semua data dari tabel baris duplikat kemudian masukkan semua data dari tabel sementara yang tidak memiliki duplikat seperti yang ditunjukkan di bawah ini.

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

   select * from table

Hapus baris duplikat menggunakan Common Table Expression (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
Md Masududzaman Khan
sumber
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
Debendra Dash
sumber
1

Dengan merujuk ke https://support.microsoft.com/en-us/help/139444/how-to-remove-duplicate-rows-from-a-table-in-sql-server

Gagasan untuk menghapus duplikat melibatkan

  • a) Melindungi baris-baris yang tidak duplikat
  • b) Mempertahankan salah satu dari banyak baris yang memenuhi syarat sebagai duplikat.

Selangkah demi selangkah

  • 1) Pertama, identifikasi baris yang memenuhi definisi duplikat dan masukkan ke tabel temp, katakan #tableAll.
  • 2) Pilih non-duplikat (baris tunggal) atau baris berbeda ke dalam tabel temp mengatakan #tableUnique.
  • 3) Hapus dari tabel sumber bergabung dengan #tableAll untuk menghapus duplikat.
  • 4) Masukkan ke dalam tabel sumber semua baris dari #tableUnique.
  • 5) Jatuhkan #tableAll dan #tableUnique
rajibdotnet
sumber
1

Jika Anda memiliki kemampuan untuk menambahkan kolom ke tabel sementara, ini adalah solusi yang berfungsi untuk saya:

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

Kemudian lakukan DELETE menggunakan kombinasi MIN dan 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
                 );

Verifikasi bahwa DELETE dilakukan dengan benar:

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 

Hasilnya seharusnya tidak memiliki baris dengan jumlah lebih besar dari 1. Akhirnya, hapus kolom rowid:

ALTER TABLE dbo.DUPPEDTABLE DROP COLUMN RowID;
jull
sumber
0

Cara lain untuk menghapus baris yang telah dipublikasikan tanpa kehilangan informasi dalam satu langkah adalah seperti berikut:

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
Tolga Gölelçin
sumber
0

Oh wow, saya merasa sangat bodoh dengan menyiapkan semua jawaban ini, mereka seperti jawaban para ahli dengan semua CTE dan tabel sementara dan lain-lain

Dan semua yang saya lakukan untuk membuatnya berfungsi hanyalah mengumpulkan kolom ID dengan menggunakan MAX.

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

CATATAN: Anda mungkin perlu menjalankannya beberapa kali untuk menghapus duplikat karena ini hanya akan menghapus satu set duplikat baris sekaligus.

kacau
sumber
Ini tidak akan berfungsi karena itu akan menghapus semua duplikat tanpa meninggalkan aslinya. OP meminta untuk menyimpan catatan asli.
0xdd
2
Tidak benar, max akan memberi Anda ID maksimum yang memenuhi syarat. Jika itu tidak benar, buktikan kasus Anda untuk memilih.
kacau
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;
Surinder Singh
sumber
Menghapus duplikat dari tabel besar (beberapa juta catatan) mungkin memakan waktu lama. Saya sarankan Anda melakukan memasukkan massal ke tabel temp dari baris yang dipilih agak menghapus. '- MENYATAKAN KODE ANDA (MENGAMBIL LINE 3RD) DENGAN CTE AS (SELECT NAME, ROW_NUMBER () LEBIH DARI (PARTISI DENGAN NAMA ORDER DENGAN NAMA) ID DARI @TB) SELECT * KE #unique_records DARI 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))
Dr.Stark
sumber