Come posso rimuovere le righe duplicate?


1285

Qual è il modo migliore per rimuovere le righe duplicate da una SQL Servertabella abbastanza grande (ovvero più di 300.000 righe)?

Le righe, ovviamente, non saranno duplicati perfetti a causa dell'esistenza del RowIDcampo identità.

MyTable

RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null

13
Suggerimento rapido per gli utenti PostgreSQL che leggono questo (molti, passando per la frequenza con cui è collegato): Pg non espone i termini CTE come viste aggiornabili, quindi non è possibile DELETE FROMutilizzare direttamente un termine CTE. Vedi stackoverflow.com/q/18439054/398670
Craig Ringer il

@CraigRinger lo stesso vale per Sybase - Qui ho raccolto le restanti soluzioni (dovrebbe essere valido anche per PG e altri: stackoverflow.com/q/19544489/1855801 (basta sostituire la ROWID()funzione con la colonna RowID, se presente)
maf-soft,

12
Solo per aggiungere un avvertimento qui. Quando si esegue qualsiasi processo di deduplicazione, ricontrollare sempre prima cosa si sta eliminando! Questa è una di quelle aree in cui è molto comune cancellare accidentalmente dati validi.
Jeff Davis,

Risposte:


1142

Supponendo che non nulli, si GROUP BYle colonne uniche, e SELECTla MIN (or MAX)RowId come riga di mantenere. Quindi, elimina tutto ciò che non aveva un ID riga:

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

Nel caso in cui si disponga di un GUID anziché di un numero intero, è possibile sostituirlo

MIN(RowId)

con

CONVERT(uniqueidentifier, MIN(CONVERT(char(36), MyGuidColumn)))

327
Funzionerebbe anche questo? DELETE FROM MyTable WHERE RowId NOT IN (SELECT MIN(RowId) FROM MyTable GROUP BY Col1, Col2, Col3);
Georg Schölly,

10
@Andriy - In SQL Server LEFT JOINè meno efficiente di NOT EXISTS sqlinthewild.co.za/index.php/2010/03/23/... Lo stesso sito confronta anche NOT INvs NOT EXISTS. sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in Su 3 penso che NOT EXISTSfunzioni meglio. Tutti e tre genereranno un piano con un self join anche se ciò può essere evitato.
Martin Smith,

12
@Martin, @Georg: Quindi ho fatto un piccolo test. È stato creato e popolato un grande tavolo come descritto qui: sqlinthewild.co.za/index.php/2010/03/23/… Sono stati quindi prodotti due SELECT, uno usando la tecnica LEFT JOIN + WHERE IS NULL, l'altro usando NOT In uno. Quindi ho proceduto con i piani di esecuzione e indovinate un po '? I costi della query erano del 18% per LEFT JOIN contro l'82% di NOT IN, una grande sorpresa per me. Avrei potuto fare qualcosa che non avrei dovuto o viceversa, che, se fosse vero, mi piacerebbe davvero saperlo.
Andriy M,

16
@ GeorgSchölly ha fornito una risposta elegante. L'ho usato su un tavolo in cui un mio bug PHP ha creato righe duplicate.
Philip Kearns,

12
Scusa ma perché DELETE MyTable FROM MyTablela sintassi è corretta? Non vedo mettere il nome della tabella subito dopo l' DELETEopzione come nella documentazione qui . Scusate se questo è ovvio per gli altri; Sono un principiante di SQL solo cercando di imparare. Ancora più importante del perché funziona: qual è la differenza tra l'inclusione del nome della tabella lì o no?
levininja,

760

Un altro modo possibile per farlo è

; 

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

Sto usando ORDER BY (SELECT 0)sopra in quanto è arbitraria quale riga conservare in caso di pareggio.

Per preservare l'ultimo RowID, ad esempio, è possibile utilizzareORDER BY RowID DESC

Piani di esecuzione

Il piano di esecuzione per questo è spesso più semplice ed efficiente di quello nella risposta accettata in quanto non richiede l'auto-join.

Piani di esecuzione

Questo non è sempre il caso comunque. Un posto in cui la GROUP BYsoluzione potrebbe essere preferita sono le situazioni in cui un aggregato di hash verrebbe scelto in preferenza di un aggregato di stream.

La ROW_NUMBERsoluzione fornirà quasi sempre lo stesso piano, mentre la GROUP BYstrategia è più flessibile.

Piani di esecuzione

Fattori che potrebbero favorire l'approccio aggregato hash sarebbero

  • Nessun indice utile sulle colonne di partizionamento
  • relativamente meno gruppi con relativamente più duplicati in ciascun gruppo

Nelle versioni estreme di questo secondo caso (se ci sono pochissimi gruppi con molti duplicati in ciascuno) si potrebbe anche considerare semplicemente l'inserimento delle righe da conservare in una nuova tabella, quindi TRUNCATEl'originale e la copia di nuovo per minimizzare la registrazione rispetto all'eliminazione di un altissima percentuale di file.


28
Se posso aggiungere: la risposta accettata non funziona con le tabelle che utilizza uniqueidentifier. Questo è molto più semplice e funziona perfettamente su qualsiasi tavolo. Grazie Martin.
BrunoLM,

15
Questa è una risposta così fantastica! Ha funzionato all'evento quando avevo rimosso il vecchio PK prima che mi rendessi conto lì dove erano duplicati. +100
Mikael Eliasson,

12
Suggerisco di porre e quindi rispondere a questa domanda (con questa risposta) su DBA.SE. Quindi possiamo aggiungerlo al nostro elenco di risposte canoniche .
Nick Chammas,

16
A differenza della risposta accettata, questo ha funzionato anche su una tabella senza chiave ( RowId) su cui confrontare.
vossad01

8
Questo non funziona su tutte le versioni del server SQL, d'altra parte
David

150

C'è un buon articolo sulla rimozione dei duplicati sul sito del supporto Microsoft. È piuttosto conservativo - ti fanno fare tutto in passaggi separati - ma dovrebbe funzionare bene su tavoli di grandi dimensioni.

Ho usato i self-join per farlo in passato, anche se probabilmente potrebbe essere ottimizzato con una clausola HAVING:

DELETE dupes
FROM MyTable dupes, MyTable fullTable
WHERE dupes.dupField = fullTable.dupField 
AND dupes.secondDupField = fullTable.secondDupField 
AND dupes.uniqueField > fullTable.uniqueField

Perfetto! ho scoperto che questo è il modo più efficiente per rimuovere le righe duplicate sulla mia vecchia versione 10.1.xx di mariadb. grazie!
Drunken M

Molto più semplice e facile da capire!
Marc,

98

La seguente query è utile per eliminare righe duplicate. La tabella in questo esempio ha IDcome colonna di identità e le colonne che hanno i dati duplicati sono Column1, Column2e 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) 

L'utilizzo seguente mostra di script di GROUP BY, HAVING, ORDER BYin una query, e restituisce i risultati con colonna duplicati e il suo conteggio.

SELECT YourColumnName,
       COUNT(*) TotalCount
FROM   YourTableName
GROUP  BY YourColumnName
HAVING COUNT(*) > 1
ORDER  BY COUNT(*) DESC 

1
Errore MySQL con il primo script "Impossibile specificare la tabella di destinazione" TableName "per l'aggiornamento nella clausola FROM"
D.Rosado,

A parte l'errore già segnalato da D.Rosado, anche la tua prima query è molto lenta. La query SELECT corrispondente ha richiesto la mia configurazione + - 20 volte più a lungo della risposta accettata.
parvus

8
@parvus - La domanda è taggata SQL Server non MySQL. La sintassi va bene in SQL Server. Anche MySQL è notoriamente cattivo nell'ottimizzare le query secondarie, vedi ad esempio qui . Questa risposta va bene in SQL Server. In effetti NOT INspesso si comporta meglio di OUTER JOIN ... NULL. Vorrei aggiungere un HAVING MAX(ID) IS NOT NULLalla query anche se semanticamente non dovrebbe essere necessario in quanto ciò può migliorare l' esempio di
Martin Smith

2
Funziona alla grande in PostgreSQL 8.4.
Nord,

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

Perché pubblicare una soluzione Postgres su una domanda di SQL Server?
Lankymart,

2
@Lankymart Perché anche gli utenti di Postgres vengono qui. Guarda il punteggio di questa risposta.
Gabriel,

2
Ho visto questo in alcune domande SQL popolari, come qui , qui e qui . L'OP ha ottenuto la sua risposta e anche tutti gli altri hanno avuto un po 'di aiuto. Nessun problema 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 

1
Ricevo questo messaggio su SQL DW azzurro: una clausola FROM non è attualmente supportata in un'istruzione DELETE.
Entro il

40

Ciò eliminerà le righe duplicate, tranne la prima riga

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

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


10
Per mysql verrà visualizzato l'errore: Codice errore: 1093. Non è possibile specificare la tabella di destinazione "Mytable" per l'aggiornamento nella clausola FROM. ma questa piccola modifica funzionerà per mysql: ELIMINA DA Mytable DOVE RowID NON È IN (SELEZIONA ID DA (SELEZIONA MIN (RowID) COME ID DA Mytable GROUP DA Col1, Col2, Col3) COME TEMP)
Ritesh

35

Preferirei CTE per eliminare le righe duplicate dalla tabella del server sql

consiglio vivamente di seguire questo articolo: http://codaffection.com/sql-server-article/delete-duplicate-rows-in-sql-server/

mantenendo l'originale

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

senza mantenere l'originale

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)

24

Per recuperare righe duplicate:

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

Per eliminare le righe duplicate:

DELETE users 
WHERE rowid NOT IN 
(SELECT MIN(rowid)
FROM users
GROUP BY name, email);      

Per gli utenti MySQL, nota che prima di tutto deve essere DELETE FROM, in secondo luogo, non funzionerà, perché non puoi SELECTdalla stessa tabella da cui stai DELETEeseguendo. In MySQL questo si spegne MySQL error 1093.
Íhor Mé,

23

Veloce e sporco per eliminare righe duplicate esatte (per tabelle di piccole dimensioni):

select  distinct * into t2 from t1;
delete from t1;
insert into t1 select *  from t2;
drop table t2;

3
Si noti che la domanda specifica in realtà una duplicazione non esatta (id della riga dueto).
Dennis Jaheruddin,

21

Preferisco la subquery \ having count (*)> 1 soluzione al join interno perché ho trovato più facile da leggere ed è stato molto facile trasformare in un'istruzione SELECT per verificare cosa sarebbe stato eliminato prima di eseguirlo.

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

Non elimina tutti i record visualizzati nella query interna. Dobbiamo rimuovere solo i duplicati e conservare l'originale.
Sandy,

3
Restituisci solo quello con l'id più basso, in base al minimo (id) nella clausola select.
James Errico,

2
Rimuovere il commento dalla prima, seconda e ultima riga della query.
James Errico,

7
Questo non pulirà tutti i duplicati. Se hai 3 righe che sono duplicate, selezionerà solo la riga con MIN (id) ed eliminerà quella, lasciando due righe rimanenti che sono duplicate.
Chloe,

2
Tuttavia, ho finito per usare questa affermazione ripetuta più e più volte, in modo che avrebbe effettivamente fatto progressi invece di avere il timeout della connessione o il computer in stato di sospensione. L'ho modificato MAX(id)per eliminare gli ultimi duplicati e l' ho aggiunto LIMIT 1000000alla query interna in modo da non dover eseguire la scansione dell'intera tabella. Ciò ha mostrato progressi molto più rapidi rispetto alle altre risposte, che sembrano sospendere per ore. Dopo che la tabella è stata potata a una dimensione gestibile, puoi terminare con le altre query. Suggerimento: assicurarsi che col1 / col2 / col3 abbia indici per il raggruppamento.
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

5
Il troncamento non funzionerà se hai riferimenti di chiave esterna a myTable.
Sameer Alibhai,

15

Ho pensato di condividere la mia soluzione poiché funziona in circostanze speciali. Nel mio caso la tabella con valori duplicati non aveva una chiave esterna (perché i valori erano duplicati da un altro db).

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: quando lavoro su cose del genere uso sempre una transazione, questo non solo assicura che tutto sia eseguito nel suo insieme, ma mi permette anche di testare senza rischiare nulla. Ma ovviamente dovresti fare un backup comunque solo per essere sicuro ...


14

Questa query ha mostrato ottime prestazioni per me:

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

ha eliminato 1 milione di righe in poco più di 30 secondi da una tabella di 2 milioni (duplicati del 50%)


14

Utilizzando CTE. L'idea è di unire su una o più colonne che formano un record duplicato e quindi rimuovere quello che ti piace:

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

1
Penso che ti manchi un AND nel tuo JOIN.
Justin R.

13

Un'altra soluzione semplice può essere trovata al link incollato qui . Questo è facile da capire e sembra essere efficace per la maggior parte dei problemi simili. È per SQL Server però, ma il concetto utilizzato è più che accettabile.

Ecco le parti pertinenti della pagina collegata:

Considera questi dati:

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

Quindi, come possiamo eliminare quei dati duplicati?

Innanzitutto, inserisci una colonna identità in quella tabella usando il seguente codice:

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

Utilizzare il seguente codice per risolverlo:

DELETE FROM dbo.ATTENDANCE WHERE AUTOID NOT IN (SELECT MIN(AUTOID) _
    FROM dbo.ATTENDANCE GROUP BY EMPLOYEE_ID,ATTENDANCE_DATE) 

1
"Facile da afferrare", "sembra essere efficace", ma non una parola su cosa consiste il metodo. Immagina solo che il collegamento non sia più valido, a che cosa servirebbe sapere che il metodo era facile da capire ed efficace? Ti preghiamo di considerare di aggiungere parti essenziali della descrizione del metodo nel tuo post, altrimenti questa non è una risposta.
Andriy M,

Questo metodo è utile per le tabelle in cui non hai ancora definito un'identità. Spesso è necessario eliminare i duplicati per definire la chiave primaria!
Jeff Davis,

@JeffDavis - La ROW_NUMBERversione funziona bene per quel caso senza la necessità di aggiungere una nuova colonna prima di iniziare.
Martin Smith,

12

Ecco un altro buon articolo sulla rimozione dei duplicati .

Discute il motivo per cui è difficile: " SQL si basa sull'algebra relazionale e non possono verificarsi duplicati nell'algebra relazionale, perché i duplicati non sono consentiti in un set. "

La soluzione della tabella temporanea e due esempi mysql.

In futuro hai intenzione di prevenirlo a livello di database o dal punto di vista dell'applicazione. Suggerirei il livello del database perché il database dovrebbe essere responsabile del mantenimento dell'integrità referenziale, gli sviluppatori causeranno problemi;)


1
SQL si basa su più set. Ma anche se fosse basato su set, queste due tuple (1, a) e (2, a) sono diverse.
Andrew,

12

Oh certo. Usa una tabella temporanea. Se vuoi una singola affermazione non molto performante che "funzioni" puoi andare con:

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)

Fondamentalmente, per ogni riga nella tabella, la sottoselezione trova il RowID superiore di tutte le righe che sono esattamente come la riga in esame. Quindi si finisce con un elenco di RowID che rappresentano le righe "originali" non duplicate.


11

Avevo una tabella in cui dovevo conservare le righe non duplicate. Non sono sicuro della velocità o dell'efficienza.

DELETE FROM myTable WHERE RowID IN (
  SELECT MIN(RowID) AS IDNo FROM myTable
  GROUP BY Col1, Col2, Col3
  HAVING COUNT(*) = 2 )

7
Questo presuppone che ci sia al massimo 1 duplicato.
Martin Smith,

Perché no HAVING COUNT(*) > 1?
Philipp M,

11

Usa questo

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

10

L'altro modo è Creare una nuova tabella con gli stessi campi e con Indice univoco . Quindi spostare tutti i dati dalla vecchia tabella alla nuova tabella . SQL Server ignora automaticamente (esiste anche un'opzione su cosa fare se ci sarà un valore duplicato: ignora, interrompi o sth) valori duplicati. Quindi abbiamo la stessa tabella senza righe duplicate. Se non si desidera l'Indice univoco, dopo i dati di trasferimento è possibile rilasciarlo .

Soprattutto per le tabelle più grandi è possibile utilizzare DTS (pacchetto SSIS per importare / esportare dati) al fine di trasferire rapidamente tutti i dati alla nuova tabella indicizzata in modo univoco. Per 7 milioni di fila ci vogliono solo pochi minuti.


9

Utilizzando la query di seguito è possibile eliminare i record duplicati in base alla singola colonna o alla colonna multipla. la query seguente viene eliminata in base a due colonne. il nome della tabella è: testinge i nomi delle colonneempno,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)

9
  1. Crea una nuova tabella vuota con la stessa struttura

  2. Eseguire query in questo modo

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) > 1
  3. Quindi eseguire questa query

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) = 1


7

Vorrei menzionare questo approccio in quanto può essere utile e funziona in tutti i server SQL: abbastanza spesso ce n'è solo uno: due duplicati e sono noti ID e numero di duplicati. In questo caso:

SET ROWCOUNT 1 -- or set to number of rows to be deleted
delete from myTable where RowId = DuplicatedID
SET ROWCOUNT 0

7

Dal livello dell'applicazione (purtroppo). Concordo sul fatto che il modo corretto per impedire la duplicazione sia a livello di database mediante l'uso di un indice univoco, ma in SQL Server 2005 un indice può contenere solo 900 byte e il mio campo varchar (2048) lo fa saltare.

Non so quanto bene avrebbe funzionato, ma penso che potresti far scattare un trigger per far rispettare questo, anche se non puoi farlo direttamente con un indice. Qualcosa di simile a:

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

Inoltre, varchar (2048) mi sembra strano (alcune cose nella vita sono 2048 byte, ma è abbastanza raro); non dovrebbe davvero essere varchar (max)?


7

Un altro modo di fare questo: -

DELETE A
FROM   TABLE A,
       TABLE B
WHERE  A.COL1 = B.COL1
       AND A.COL2 = B.COL2
       AND A.UNIQUEFIELD > B.UNIQUEFIELD 

Cosa c'è di diverso rispetto a questa risposta esistente dal 20 agosto 2008? - 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
    );

Ciao Teena, hai perso la tabella nome Alice T1 dopo il commento di eliminazione, altrimenti si verificherebbe un'eccezione di sintassi.
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)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.