Jak mogę usunąć zduplikowane wiersze?

1284

Jaki jest najlepszy sposób na usunięcie zduplikowanych wierszy z dość dużej SQL Servertabeli (tj. Ponad 300 000 wierszy)?

Wiersze oczywiście nie będą idealnymi duplikatami ze względu na istnienie RowIDpola tożsamości.

Mój stół

RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null
Seibar
źródło
13
Szybka wskazówka dla użytkowników PostgreSQL, którzy to czytają (wiele, w zależności od tego, jak często jest to powiązane): Pg nie ujawnia warunków CTE jako poglądów, które można aktualizować, więc nie możesz DELETE FROMbezpośrednio terminu CTE. Zobacz stackoverflow.com/q/18439054/398670
Craig Ringer
@CraigRinger to samo dotyczy Sybase - zebrałem tutaj pozostałe rozwiązania (powinno być również ważne dla PG i innych: stackoverflow.com/q/19544489/1855801 (wystarczy zastąpić ROWID()funkcję kolumną RowID, jeśli istnieje)
maf-soft
12
Aby dodać tutaj zastrzeżenie. Podczas uruchamiania procesu usuwania duplikatów zawsze sprawdź najpierw, co usuwasz! Jest to jeden z tych obszarów, w których bardzo często przypadkowo usuwa się dobre dane.
Jeff Davis,

Odpowiedzi:

1142

Zakładając, że nie ma wartości null, to GROUP BYunikalne kolumny i rowid jako rzędu zachować. Następnie po prostu usuń wszystko, co nie miało identyfikatora wiersza:SELECTMIN (or MAX)

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

Jeśli masz identyfikator GUID zamiast liczby całkowitej, możesz go wymienić

MIN(RowId)

z

CONVERT(uniqueidentifier, MIN(CONVERT(char(36), MyGuidColumn)))
Mark Brackett
źródło
327
Czy to też zadziała? DELETE FROM MyTable WHERE RowId NOT IN (SELECT MIN(RowId) FROM MyTable GROUP BY Col1, Col2, Col3);
Georg Schölly,
10
@Andriy - W SQL Server LEFT JOINjest mniej wydajny niż NOT EXISTS sqlinthewild.co.za/index.php/2010/03/23/... Ta sama strona porównuje również NOT INz NOT EXISTS. sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in Spośród 3, moim zdaniem, NOT EXISTSdziała najlepiej. Wszystkie trzy wygenerują plan z samozłączeniem, którego można jednak uniknąć.
Martin Smith
12
@Martin, @Georg: Więc zrobiłem mały test. Utworzono duży stół i wypełniono go zgodnie z opisem tutaj: sqlinthewild.co.za/index.php/2010/03/23/ ... Następnie wyprodukowano dwa SELECT, jeden przy użyciu techniki LEFT JOIN + WHERE IS NULL, drugi przy użyciu NOT W jednym. Potem przystąpiłem do planów wykonania i zgadnij co? Koszt zapytania wyniósł 18% dla LEFT JOIN wobec 82% dla NOT IN, co jest dla mnie dużą niespodzianką. Mógłbym zrobić coś, czego nie powinienem mieć, i odwrotnie, co, jeśli to prawda, naprawdę chciałbym wiedzieć.
Andriy M
16
@ GeorgSchölly udzielił eleganckiej odpowiedzi. Użyłem go na stole, na którym mój błąd PHP stworzył duplikaty wierszy.
Philip Kearns
12
Przepraszamy, ale dlaczego DELETE MyTable FROM MyTablepoprawna składnia? Nie widzę umieszczania nazwy tabeli zaraz po opcji DELETEjako opcji w dokumentacji tutaj . Przepraszam, jeśli jest to oczywiste dla innych; Jestem początkującym w SQL tylko próbuję się uczyć. Co ważniejsze niż dlaczego to działa: jaka jest różnica między podaniem nazwy tabeli, czy nie?
levininja
760

Innym możliwym sposobem na to jest

; 

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

Używam ORDER BY (SELECT 0)powyższego, ponieważ jest to dowolny wiersz, który należy zachować w przypadku remisu.

Aby zachować najnowszą RowID, na przykład możesz użyćORDER BY RowID DESC

Plany wykonania

Plan wykonania tego jest często prostszy i wydajniejszy niż w przyjętej odpowiedzi, ponieważ nie wymaga samodzielnego dołączenia.

Plany wykonania

Jednak nie zawsze tak jest. Jednym z miejsc, w których GROUP BYrozwiązanie może być preferowane, są sytuacje, w których agregat skrótu byłby wybierany zamiast agregatu strumienia.

ROW_NUMBERRozwiązanie będzie zawsze daje dość dużo ten sam plan, podczas gdy GROUP BYstrategia jest bardziej elastyczny.

Plany wykonania

Czynniki, które mogą sprzyjać podejściu agregacji skrótów, to:

  • Brak przydatnego indeksu w kolumnach partycjonowania
  • stosunkowo mniej grup z relatywnie większą liczbą duplikatów w każdej grupie

W skrajnych wersjach tego drugiego przypadku (jeśli jest bardzo niewiele grup z wieloma duplikatami w każdej), można również rozważyć wstawienie wierszy w celu zachowania w nowej tabeli, a następnie TRUNCATEskopiowanie oryginału i skopiowanie ich z powrotem, aby zminimalizować rejestrowanie w porównaniu do usunięcia bardzo duża część rzędów.

Martin Smith
źródło
28
Jeśli mogę dodać: zaakceptowana odpowiedź nie działa z tabelami, które używają uniqueidentifier. Ten jest o wiele prostszy i działa idealnie na każdym stole. Dzięki Martin.
BrunoLM,
15
To niesamowita odpowiedź! To zadziałało, gdy usunąłem stary PK, zanim zdałem sobie sprawę, gdzie są duplikaty. +100
Mikael Eliasson
12
Proponuję zadać, a następnie odpowiedzieć na to pytanie (z tą odpowiedzią) na DBA.SE. Następnie możemy dodać go do naszej listy odpowiedzi kanonicznych .
Nick Chammas
16
W przeciwieństwie do przyjętej odpowiedzi, działało to również na stole, który nie miał klucza ( RowId) do porównania.
vossad01
8
Z drugiej strony ten nie działa na wszystkich wersjach serwera SQL
David
150

W witrynie pomocy technicznej Microsoft znajduje się dobry artykuł na temat usuwania duplikatów . To dość konserwatywne - każą ci robić wszystko w osobnych krokach - ale powinno dobrze działać na dużych stołach.

W przeszłości korzystałem z autozłączeń, chociaż prawdopodobnie można to zrobić z klauzulą ​​HAVING:

DELETE dupes
FROM MyTable dupes, MyTable fullTable
WHERE dupes.dupField = fullTable.dupField 
AND dupes.secondDupField = fullTable.secondDupField 
AND dupes.uniqueField > fullTable.uniqueField
Jon Galloway
źródło
doskonały! znalazłem, że jest to najbardziej skuteczny sposób na usunięcie duplikatów wierszy w mojej starej wersji mariadb 10.1.xx. Dziękuję Ci!
Pijany M
O wiele prostsze i łatwiejsze do zrozumienia!
Marc
98

Poniższe zapytanie jest przydatne do usunięcia zduplikowanych wierszy. Tabela w tym przykładzie ma IDjako kolumnę tożsamości, a kolumny ze zduplikowanymi danymi to Column1, Column2i 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) 

Wykorzystanie Poniżej przedstawiono scenariusz GROUP BY, HAVING, ORDER BYw jednym zapytaniu i zwraca wyniki z duplikatu kolumny i jej liczenia.

SELECT YourColumnName,
       COUNT(*) TotalCount
FROM   YourTableName
GROUP  BY YourColumnName
HAVING COUNT(*) > 1
ORDER  BY COUNT(*) DESC 
gngolakia
źródło
1
Błąd MySQL z pierwszym skryptem „Nie można określić tabeli docelowej„ TableName ”do aktualizacji w klauzuli FROM”
D.Rosado,
Oprócz zgłoszonego już błędu D.Rosado, twoje pierwsze zapytanie jest również bardzo wolne. Odpowiednie zapytanie SELECT zajęło moją konfigurację + - 20 razy dłużej niż zaakceptowana odpowiedź.
parvus
8
@parvus - Pytanie jest oznaczone jako SQL Server, a nie MySQL. Składnia jest dobra w SQL Server. Również MySQL jest notorycznie kiepski w optymalizacji sub-zapytań, patrz na przykład tutaj . Ta odpowiedź jest w porządku w SQL Server. W rzeczywistości NOT INczęsto działa lepiej niż OUTER JOIN ... NULL. Dodałbym HAVING MAX(ID) IS NOT NULLdo zapytania, chociaż chociaż semantycznie nie powinno to być konieczne, ponieważ może to poprawić przykład
Martin Smith
2
Działa świetnie w PostgreSQL 8.4.
północ
63
delete t1
from table t1, table t2
where t1.columnA = t2.columnA
and t1.rowid>t2.rowid

Postgres:

delete
from table t1
using table t2
where t1.columnA = t2.columnA
and t1.rowid > t2.rowid
SoftwareGeek
źródło
Po co publikować rozwiązanie Postgres na pytanie dotyczące programu SQL Server?
Lankymart
2
@Lankymart Ponieważ tutaj również przychodzą użytkownicy Postgres. Spójrz na wynik tej odpowiedzi.
Gabriel
2
Widziałem to w niektórych popularnych pytaniach SQL, tak jak tutaj , tutaj i tutaj . OP otrzymał odpowiedź i wszyscy inni też otrzymali pomoc. Bez problemu IMHO.
Gabriel
44
DELETE LU 
FROM   (SELECT *, 
               Row_number() 
                 OVER ( 
                   partition BY col1, col1, col3 
                   ORDER BY rowid DESC) [Row] 
        FROM   mytable) LU 
WHERE  [row] > 1 
Jithin Shaji
źródło
1
Otrzymuję ten komunikat na lazurowym SQL DW: Klauzula FROM nie jest obecnie obsługiwana w instrukcji DELETE.
Amit
40

Spowoduje to usunięcie duplikatów wierszy, z wyjątkiem pierwszego wiersza

DELETE
FROM
    Mytable
WHERE
    RowID NOT IN (
        SELECT
            MIN(RowID)
        FROM
            Mytable
        GROUP BY
            Col1,
            Col2,
            Col3
    )

Patrz ( http://www.codeproject.com/Articles/157977/Remove-Duplicate-Rows-from-a-Table-in-SQL-Server )

Syed Mohamed
źródło
10
W przypadku mysql wyświetli błąd: Kod błędu: 1093. Nie można określić tabeli docelowej „Mytable” do aktualizacji w klauzuli FROM. ale ta niewielka zmiana zadziała w przypadku mysql: USUŃ Z Mytable, GDZIE NIE IDENTYFIKOWANO RowID (WYBIERZ ID Z (WYBIERZ MIN (RowID) AS ID Z GRUPY Mytable WG Col1, Col2, Col3) JAKO TEMP)
Ritesh
35

Wolę CTE do usuwania duplikatów wierszy z tabeli serwera SQL

zdecydowanie zalecamy śledzenie tego artykułu :: http://codaffection.com/sql-server-article/delete-duplicate-rows-in-sql-server/

zachowując oryginał

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

bez zachowania oryginalności

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.
źródło
24

Aby pobrać zduplikowane wiersze:

SELECT
name, email, COUNT(*)
FROM 
users
GROUP BY
name, email
HAVING COUNT(*) > 1

Aby usunąć zduplikowane wiersze:

DELETE users 
WHERE rowid NOT IN 
(SELECT MIN(rowid)
FROM users
GROUP BY name, email);      
Shaini Sinha
źródło
Dla użytkowników MySQL, pamiętaj, że po pierwsze musi to być DELETE FROM, po drugie, nie będzie działać, ponieważ nie możesz SELECTz tej samej tabeli, z której DELETEpochodzi. W MySQL to się zaczyna MySQL error 1093.
Íhor Mé
23

Szybkie i brudne, aby usunąć dokładnie zduplikowane wiersze (dla małych tabel):

select  distinct * into t2 from t1;
delete from t1;
insert into t1 select *  from t2;
drop table t2;
JuanJo
źródło
3
Zauważ, że pytanie faktycznie określa niedokładne powielanie (dueto id wiersza).
Dennis Jaheruddin
21

Wolę podzapytanie \ posiadające liczbę (*)> 1 rozwiązania dla złączenia wewnętrznego, ponieważ łatwiej mi było czytać i bardzo łatwo było zmienić instrukcję SELECT, aby sprawdzić, co zostanie usunięte przed uruchomieniem.

--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
--)
James Errico
źródło
Nie usuwa wszystkich rekordów wyświetlanych w wewnętrznym zapytaniu. Musimy usunąć tylko duplikaty i zachować oryginał.
Sandy,
3
Zwracasz tylko ten o najniższym identyfikatorze na podstawie min (id) w klauzuli select.
James Errico
2
Usuń komentarz z pierwszego, drugiego i ostatniego wiersza zapytania.
James Errico
7
To nie wyczyści wszystkich duplikatów. Jeśli masz 3 wiersze, które są duplikatami, wybierze tylko wiersz z MIN (id) i usunie ten, pozostawiając dwa wiersze, które są duplikatami.
Chloe,
2
Niemniej jednak skończyło się na tym, że wielokrotnie powtarzałem to oświadczenie, aby faktycznie robił postępy, zamiast przekroczenia limitu czasu połączenia lub uśpienia komputera. Zmieniłem to, aby MAX(id)wyeliminować te ostatnie duplikaty, i dodałem LIMIT 1000000do wewnętrznego zapytania, aby nie musiało skanować całej tabeli. To pokazało postęp znacznie szybciej niż inne odpowiedzi, które wydawałyby się zawieszone przez wiele godzin. Po przycięciu tabeli do możliwego do zarządzania rozmiaru, możesz zakończyć inne zapytania. Wskazówka: upewnij się, że col1 / col2 / col3 ma wskaźniki dla grup według.
Chloe,
17
SELECT  DISTINCT *
      INTO tempdb.dbo.tmpTable
FROM myTable

TRUNCATE TABLE myTable
INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable
DROP TABLE tempdb.dbo.tmpTable
heta77
źródło
5
Obcinanie nie będzie działać, jeśli masz odwołania do obcej tabeli myTable.
Sameer Alibhai
15

Myślałem, że podzielę się moim rozwiązaniem, ponieważ działa w szczególnych okolicznościach. W moim przypadku tabela ze zduplikowanymi wartościami nie miała klucza obcego (ponieważ wartości zostały zduplikowane z innej bazy danych).

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

PS: pracując nad takimi rzeczami zawsze korzystam z transakcji, nie tylko zapewnia to, że wszystko jest wykonywane jako całość, ale pozwala mi także na testowanie bez ryzyka. Ale oczywiście powinieneś zrobić kopię zapasową, aby się upewnić ...

Ruben Verschueren
źródło
14

To zapytanie pokazało mi bardzo dobrą wydajność:

DELETE tbl
FROM
    MyTable tbl
WHERE
    EXISTS (
        SELECT
            *
        FROM
            MyTable tbl2
        WHERE
            tbl2.SameValue = tbl.SameValue
        AND tbl.IdUniqueValue < tbl2.IdUniqueValue
    )

usunął 1 mln wierszy w nieco ponad 30 sekund z tabeli 2 mln (50% duplikatów)

Draško
źródło
14

Korzystanie z CTE. Chodzi o to, aby dołączyć do jednej lub więcej kolumn, które tworzą zduplikowany rekord, a następnie usunąć dowolne:

;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;
Ostati
źródło
1
Myślę, że brakuje ci AND w swoim DOŁĄCZ.
Justin R.
13

Jeszcze jedno łatwe rozwiązanie można znaleźć pod linkiem wklejonym tutaj . Ten jest łatwy do uchwycenia i wydaje się być skuteczny w przypadku większości podobnych problemów. Dotyczy to jednak SQL Server, ale zastosowana koncepcja jest więcej niż akceptowalna.

Oto odpowiednie fragmenty z połączonej strony:

Rozważ te dane:

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

Jak więc usunąć te zduplikowane dane?

Najpierw wstaw kolumnę tożsamości do tej tabeli przy użyciu następującego kodu:

ALTER TABLE dbo.ATTENDANCE ADD AUTOID INT IDENTITY(1,1)  

Użyj następującego kodu, aby go rozwiązać:

DELETE FROM dbo.ATTENDANCE WHERE AUTOID NOT IN (SELECT MIN(AUTOID) _
    FROM dbo.ATTENDANCE GROUP BY EMPLOYEE_ID,ATTENDANCE_DATE) 
Nitish Pareek
źródło
1
„Łatwy do uchwycenia”, „wydaje się być skuteczny”, ale ani słowa o tym, na czym polega metoda. Wyobraź sobie, że link staje się nieważny, jaki pożytek miałby wtedy wiedzieć, że metoda była łatwa do uchwycenia i skuteczna? Zastanów się nad dodaniem do wpisu istotnych części opisu metody, w przeciwnym razie nie jest to odpowiedź.
Andriy M
Ta metoda jest przydatna w przypadku tabel, w których nie zdefiniowano jeszcze tożsamości. Często musisz pozbyć się duplikatów, aby zdefiniować klucz podstawowy!
Jeff Davis,
@JeffDavis - ROW_NUMBERWersja działa dobrze w tym przypadku bez konieczności przechodzenia do dodawania nowej kolumny przed rozpoczęciem.
Martin Smith
12

Oto kolejny dobry artykuł na temat usuwania duplikatów .

Omówiono, dlaczego trudno: „ SQL opiera się na algebrze relacyjnej, a duplikaty nie mogą występować w algebrze relacyjnej, ponieważ duplikaty nie są dozwolone w zestawie ”.

Rozwiązanie tabeli temp i dwa przykłady mysql.

W przyszłości zamierzasz temu zapobiec na poziomie bazy danych lub z perspektywy aplikacji. Sugerowałbym poziom bazy danych, ponieważ twoja baza danych powinna być odpowiedzialna za utrzymanie integralności referencyjnej, programiści po prostu powodują problemy;)

Craig
źródło
1
SQL oparty jest na wielu zestawach. Ale nawet jeśli był oparty na zestawach, te dwie krotki (1, a) i (2, a) są różne.
Andrew
12

Oh, pewnie. Użyj tabeli temp. Jeśli potrzebujesz pojedynczej, mało wydajnej instrukcji, która „działa”, możesz użyć:

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)

Zasadniczo dla każdego wiersza w tabeli wybór podrzędny znajduje górny identyfikator wiersza wszystkich wierszy, które są dokładnie takie same jak rozważany wiersz. W rezultacie otrzymujesz listę RowID, które reprezentują „oryginalne” niepowielone wiersze.

Jacob Proffitt
źródło
11

Miałem tabelę, w której musiałem zachować niepowielone wiersze. Nie jestem pewien szybkości ani wydajności.

DELETE FROM myTable WHERE RowID IN (
  SELECT MIN(RowID) AS IDNo FROM myTable
  GROUP BY Col1, Col2, Col3
  HAVING COUNT(*) = 2 )
chrismar035
źródło
7
Zakłada się, że istnieje maksymalnie 1 duplikat.
Martin Smith
Dlaczego nie HAVING COUNT(*) > 1?
Philipp M
11

Użyj tego

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
Haris
źródło
10

Innym sposobem jest utworzenie nowej tabeli z tymi samymi polami i unikalnym indeksem . Następnie przenieś wszystkie dane ze starej tabeli do nowej tabeli . Automatycznie ignoruj ​​SQL SERVER (istnieje również opcja, co zrobić, jeśli pojawi się zduplikowana wartość: zignoruj, przerwij lub sth) zduplikowane wartości. Mamy więc tę samą tabelę bez zduplikowanych wierszy. Jeśli nie chcesz Unique Index, po przesłaniu danych możesz go upuścić .

Szczególnie w przypadku większych tabel możesz użyć DTS (pakiet SSIS do importu / eksportu danych), aby szybko przenieść wszystkie dane do nowej tabeli o unikalnym indeksowaniu. W przypadku 7 milionów wierszy zajmuje to tylko kilka minut.

İsmail Yavuz
źródło
9

Korzystając z poniższego zapytania, możemy usunąć zduplikowane rekordy na podstawie pojedynczej kolumny lub wielu kolumn. poniższe zapytanie jest usuwane na podstawie dwóch kolumn. nazwa tabeli to: testingi nazwy kolumnempno,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)
Sudhakar NV
źródło
9
  1. Utwórz nowy pusty stół o tej samej strukturze

  2. Wykonaj zapytanie w ten sposób

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) > 1
  3. Następnie wykonaj to zapytanie

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) = 1
shA.t
źródło
9

Jest to najprostszy sposób na usunięcie duplikatu rekordu

 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

Harikesh Yadav
źródło
Dlaczego ktoś to popiera? Jeśli masz więcej niż dwa takie same identyfikatory, to NIE zadziała. Zamiast tego napisz: usuń z tblemp, gdzie nie ma identyfikatora (wybierz min (id) z grupy tblemp według tytułu)
crellee
7

Chciałbym wspomnieć o tym podejściu, ponieważ może być pomocne i działa na wszystkich serwerach SQL: Bardzo często jest tylko jeden - dwa duplikaty, a identyfikatory i liczba duplikatów są znane. W tym przypadku:

SET ROWCOUNT 1 -- or set to number of rows to be deleted
delete from myTable where RowId = DuplicatedID
SET ROWCOUNT 0
Evgueny Sedov
źródło
7

Z poziomu aplikacji (niestety). Zgadzam się, że właściwy sposób zapobiegania duplikacji jest na poziomie bazy danych dzięki zastosowaniu unikalnego indeksu, ale w SQL Server 2005 indeks może mieć tylko 900 bajtów, a moje pole varchar (2048) to zdmuchuje.

Nie wiem, jak dobrze by to działało, ale myślę, że możesz napisać wyzwalacz, aby to wymusić, nawet jeśli nie możesz tego zrobić bezpośrednio za pomocą indeksu. Coś jak:

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

Ponadto, varchar (2048) brzmi dla mnie podejrzanie (niektóre rzeczy w życiu to 2048 bajtów, ale jest to dość rzadkie); czy to naprawdę nie powinno być varchar (max)?

DrPizza
źródło
7

Kolejny sposób to zrobić:

DELETE A
FROM   TABLE A,
       TABLE B
WHERE  A.COL1 = B.COL1
       AND A.COL2 = B.COL2
       AND A.UNIQUEFIELD > B.UNIQUEFIELD 
yuvi
źródło
Czym różni się ta istniejąca odpowiedź z 20 sierpnia 2008 r.? - stackoverflow.com/a/18934/692942
Lankymart
7
DELETE
FROM
    table_name T1
WHERE
    rowid > (
        SELECT
            min(rowid)
        FROM
            table_name T2
        WHERE
            T1.column_name = T2.column_name
    );
Teena
źródło
Cześć Teena, brakowało Ci tabeli Alice nazwa T1 po usuniętym komentarzu, w przeciwnym razie spowoduje to wyjątek składniowy.
Nagaraj M
6
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)
AnandPhadke
źródło
6

Chcę wyświetlić podgląd wierszy, które chcesz usunąć, i zachowaj kontrolę nad tym, który z duplikatów chcesz zachować. Zobacz 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
Lauri Lubi
źródło