SQL Server'da yinelenen satırlar nasıl silinir?


415

Mevcut olmayan yinelenen satırları nasıl silebilirimunique row id ?

Benim masam

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

Yinelenen kaldırma işleminden sonra aşağıdakilerle birlikte kalmak istiyorum:

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

Birkaç sorgu denedim ama istenen sonucu alamadım gibi onlar bir satır kimliği olması gerektiğini düşünüyorum. Örneğin:

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

5
Bu, ilk bağlantının bir kopyası değildir. Bu soruda satır kimliği yoktur ve bağlı soruda satır kimliği vardır. Çok farklı.
Alien Technology

'MAX / MIN' gibi birleştirilmiş bir işleve sahip olmak için 'GROUP BY id HAVING' tablosundan SELECT id öğesini değiştirin ve çalışması gerekir.
berbat

Yanıtlar:


785

CTEs gibi ve ROW_NUMBERkombine iki bizi bu yüzden sadece değiştirmek silindi (veya güncellenmiş) gereken sütunları görmesine izin olarak DELETE FROM CTE...için 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 (sonuç farklıdır; bunun sizin tarafınızdaki bir yazım hatası nedeniyle olduğunu varsayıyorum)

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

Bu örnek, yinelenenleri col1nedeniyle tek bir sütuna göre belirler PARTITION BY col1. Birden çok sütun eklemek istiyorsanız, bunları aşağıdakilere eklemeniz yeterlidir PARTITION BY:

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

2
Harika bir cevap için teşekkürler. Buna karşılık MSFT'nin çok karmaşık bir yanıtı var: stackoverflow.com/questions/18390574/…
Barka

2
@ omachu23: bu durumda önemli değil, ancak CTE'de ( AND COl1='John') dışında daha verimli olduğunu düşünüyorum . Normalde filtreyi CTE'ye uygulamanız gerekir.
Tim Schmelter

1
@ omachu23: CTE'de (sipariş dışında) herhangi bir SQL kullanabilirsiniz, bu nedenle Johns: 'a göre filtrelemek istiyorsanız ...FROM dbo.Table1 WHERE Col1='John'. İşte keman: sqlfiddle.com/#!6/fae73/744/0
Tim Schmelter

1
En kolay çözüm sadece buradaset rowcount 1 delete from t1 where col1=1 and col2=1 görüldüğü gibi olabilir
Zorgarath

15
Bu yanıt yalnızca col1'de yinelenen satırları siler. "Select" to "partition by" sütunlarını ekleyin, örneğin yanıttaki seçimi kullanarak: RN = ROW_NUMBER () OVER (PARTITION BY col1, col2, col3, col4, col5, col6, col7 ORDER BY col1)
rlee

158

Ben sql sunucu tablosundan yinelenen satırları silmek için CTE tercih ediyorum

bu makaleyi izlemenizi kesinlikle öneririz :: http://codaffection.com/sql-server-article/delete-duplicate-rows-in-sql-server/

orijinal tutarak

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

orijinali saklamadan

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
Pencereleme işlevi harika bir çözümdür.
Robert Casey

2
Biraz kafam karıştı. Orijinal tablodan değil CTE'den sildiniz. Peki nasıl çalışır?
Bigeyes

8
@Bigeyes kayıtları CTE'den silerek karşılık gelen kayıtları gerçek fiziksel tablodan kaldıracaktır (çünkü CTE gerçek kayıtlara referans içerdiğinden).
Shamseer K

Bu yazıya kadar bunun böyle olduğu hakkında hiçbir fikrim yoktu ... Teşekkür ederim
Zakk Diaz

1
Neden hem orijinali hem de kopyasını silmek istersiniz? Neden sadece kopyayı kaldırmak ve diğerini tutmak istemeyeceğinizi anlamıyorum.
Zengin

52

Kullanmadan CTEve burada ROW_NUMBER()sadece MAXfonksiyonu ile grup kullanarak kayıtları silebilirsiniz burada ve örnek

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

4
Bu sorgu, yinelenmeyen kayıtları silecektir.
Derek Smalls

8
Bu iyi çalışıyor, teşekkürler. @DerekSmalls bu yinelenmeyen kayıtlarımı kaldırmaz.
monteirobrena

1
Veya orijinal kayıtları kullanarak tutabilirsinizMIN(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
)

Şu konuma yeniden yazamazdınız: nereye id in (max (id) seçin ... count (*)> 1)?
Brent

1
Sahip olmanın veya birliğin kullanılmasına gerek olmadığına inanmıyorum, bu yeterli olacaktır: kimliğin olmadığı aramadan silin (url'ye göre arama grubundan min (id) seçin)
Christopher Yang

9

Lütfen aşağıdaki silme yolunu da inceleyin.

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)

Adlandırılmış bir örnek tablo oluşturdu @tableve verilen verilerle yükledi.

resim açıklamasını buraya girin

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

resim açıklamasını buraya girin

Not: tüm sütunları veriyoruz ise Partition bykısmen, daha sonra order bypek önemi yok.

Biliyorum, soru üç yıl önce soruldu ve cevabım Tim'in yayınladığı şeyin başka bir versiyonudur, ancak gönderilmesi herkes için yararlıdır.


9

Yabancı anahtarlar gibi referansınız yoksa, bunu yapabilirsiniz. Kavram kanıtlarını test ederken bunu çok yapıyorum ve test verileri çoğaltılıyor.

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

INTO [newTable]

Nesne gezginine gidin ve eski tabloyu silin.

Yeni tabloyu eski tablonun adıyla yeniden adlandırın.


Giriş materyallerimde öğrendiğim ve kullandığım en basit yol budur.
eric

7

Microsoft, kopyaları kaldırma hakkında vey ry düzgün bir kılavuz vardır. Check out http://support.microsoft.com/kb/139444

Kısacası, silmek için sadece birkaç satırınız olduğunda kopyaları silmenin en kolay yolu:

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

myprimarykey satırın tanımlayıcısıdır.

Satır sayısını 1 olarak ayarladım çünkü yinelenen iki satırım vardı. Eğer 3 satır çoğaltılmış olsaydı ben gördüm ilk iki siler ve tablo t1 sadece bir tane bırakır böylece rowcount 2 olarak ayarlamış olurdu .

Umarım herkese yardımcı olur


1
10 bin satırım varsa kaç satırı çoğalttığımı nasıl bilebilirim?
Fearghal

@Fearghal "birincilKey'i seç, myTable grubundan birincilKey tarafından say (*);"
oabarca

1
Ancak, değişen sayıda yinelenen satır varsa ne olur? yani satır a 2 kaydı ve satır b 5 kaydı vardır ve satır c yinelenen kaydı yoktur
termit

1
@ user2070775 Tüm satırların yalnızca bir alt kümesinde yinelenmeler varsa ve bu yinelemelerden bazıları yinelenirse iki, üç veya dört kez yinelenirse?
termit

@ user2070775 "Silinecek birkaç satır" dediğiniz bölümü kaçırdım. Ayrıca sayfadaki set rowcount hakkında, sql'nin gelecekteki sürümlerinde güncelleme veya silme ifadelerini etkilemeyeceğine dair bir uyarı var
thermite

6

Kullanmaya çalışmak:

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

resim açıklamasını buraya girin


4

Yukarıda önerilen çözümü denedikten sonra, bu küçük orta tablolar için çalışır. Çok büyük tablolar için bu çözümü önerebilirim. çünkü yinelemelerde çalışır.

  1. Tüm bağımlılık görünümlerini LargeSourceTable
  2. sql management studio kullanarak bağımlılıkları bulabilirsiniz, masaya sağ tıklayın ve "Bağımlılıkları Görüntüle" yi tıklayın
  3. Tabloyu yeniden adlandırın:
  4. sp_rename 'LargeSourceTable', 'LargeSourceTable_Temp'; GO
  5. LargeSourceTableTekrar oluşturun , ancak şimdi çoğaltmaları tanımlayan tüm sütunları içeren bir birincil anahtar ekleyinWITH (IGNORE_DUP_KEY = ON)
  6. Örneğin:

    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. Yeni oluşturulan tablo için ilk etapta bıraktığınız görünümleri tekrar oluşturun

  8. Şimdi, aşağıdaki sql betiğini çalıştırın, sonuçları sayfa başına 1.000.000 satırda göreceksiniz, sonuçları daha sık görmek için sayfa başına satır numarasını değiştirebilirsiniz.

  9. IDENTITY_INSERTSütunlardan biri aynı zamanda kopyaladığım otomatik artımlı kimlik içerdiğinden açıp kapadığımı unutmayın

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

İki çözüm var mysql:

A) kullanarak Sil yinelenen satırlar DELETE JOINaçıklama

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

Bu sorgu kişiler tablosuna iki kez başvurur, bu nedenle tablo diğer adını t1ve kullanır t2.

Çıktı:

1 Sorgu TAMAM, 4 satır etkilendi (0.10 sn)

Yinelenen satırları silmek ve tutmak istediğinizde lowest id, aşağıdaki ifadeyi kullanabilirsiniz:

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

   

B) Ara tablo kullanarak yinelenen satırları silme

Aşağıda, bir ara tablo kullanarak yinelenen satırları kaldırma adımları gösterilmektedir:

    1. Yinelenen satırları silmek istediğiniz orijinal tabloyla aynı yapıda yeni bir tablo oluşturun.

    2. Orijinal tablodan hemen tabloya farklı satırlar ekleyin.

    3. Orijinal tablodan hemen tabloya farklı satırlar ekleyin.

 

Adım 1. Yapısı orijinal tablo ile aynı olan yeni bir tablo oluşturun:

CREATE TABLE source_copy LIKE source;

Adım 2. Orijinal tablodan yeni tabloya farklı satırlar ekleyin:

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

Adım 3. Orijinal tabloyu bırakın ve hemen tabloyu orijinal tabloyla yeniden adlandırın

DROP TABLE source;
ALTER TABLE source_copy RENAME TO source;

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

Yinelenen kayıtları alanlara göre gruplandırmanız, ardından kayıtlardan birini tutmanız ve geri kalanını silmeniz gerekir. Örneğin:

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

Büyük bir tablodan (birkaç milyon kayıt) yinelenenleri silmek uzun sürebilir. Silmek yerine, seçilen satırların geçici tablosuna toplu bir ekleme yapmanızı öneririm.

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

Bunu yapmak için en basit yolu sql sunucusunda birçok yolla yapılabilir: Yinelenen satır tablosundan yeni geçici tabloya farklı satırları ekleyin. Ardından, yinelenen satır tablosundaki tüm verileri silin ve aşağıda gösterildiği gibi yinelenenleri olmayan geçici tablodaki tüm verileri ekleyin.

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

   select * from table

Ortak Tablo İfadesi (CTE) kullanarak yinelenen satırları silme

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/tr-tr/help/139444/how-to-remove-duplicate-rows-from-a-table-in-sql-server referansı ile

Yinelenenleri kaldırma fikri

  • a) Yinelenmeyen satırların korunması
  • b) Birlikte yinelenen birçok kaliteden birini saklayın.

Adım adım

  • 1) Öncelikle yinelenen tanımını karşılayan satırları belirleyin ve bunları geçici tabloya ekleyin, #tableAll deyin.
  • 2) Geçici tabloya yinelenmeyen (tek sıralı) veya ayrı satırları #tableUnique deyin.
  • 3) Kopyaları silmek için #tableAll kaynağına katılan kaynak tablodan sil.
  • 4) #tableUnique kaynağındaki tüm satırları kaynak tabloya ekleyin.
  • 5) #tableTüm ve #tableUnique Bırak

1

Geçici olarak tabloya bir sütun ekleme olanağınız varsa, bu benim için çalışan bir çözümdü:

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

Ardından MIN ve GROUP BY kombinasyonunu kullanarak bir DELETE işlemi gerçekleştirin

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

DELETE öğesinin düzgün çalıştığını doğrulayın:

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 

Sonuçta, sayısı 1'den büyük olan satırlar olmamalıdır. Son olarak, satır kimliği sütununu kaldırın:

ALTER TABLE dbo.DUPPEDTABLE DROP COLUMN RowID;

0

Bir adımda bilgi kaybetmeden yayımlanmış satırları kaldırmanın başka bir yolu şudur:

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

Oh vay, tüm bu cevapları hazırlayarak kendimi çok aptal hissediyorum, tüm CTE ve geçici tablo vb.İle uzmanların cevabı gibi.

Ve onu çalıştırmak için yaptığım tek şey MAX kullanarak kimlik sütununu toplandı.

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

NOT: yinelenenleri kaldırmak için birden çok kez çalıştırmanız gerekebilir, çünkü bu aynı anda yalnızca bir yinelenen satır kümesini siler.


Orijinalleri terk etmeden tüm kopyaları kaldıracağından bu çalışmaz. OP, orijinal kayıtların korunmasını istiyor.
0xdd

2
Doğru değil, max size durumu sağlayan maksimum kimliği verecektir. Bu doğru değilse, aşağı oylama için davanızı kanıtlayın.
berbat

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;

Büyük bir tablodan (birkaç milyon kayıt) yinelenenleri silmek uzun sürebilir. Seçili satırların geçici tablosuna toplu bir ekleme yapmanızı silmenizi öneririm. '- KODUNUZU YAZMAK (3. SATIR NOTUNU ALIN) CTE OLARAK (SEÇİN ADI, ROW_NUMBER () ÜZERİNDE (İSİM SİPARİŞ ADI İLE BÖLÜNMESİ) ID @TB'DEN KİMİ SEÇİN * * #NONE_records'TEN CTE ID NEREDE = 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))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.