Comment supprimer des lignes en double dans SQL Server?

416

Comment supprimer des lignes en double là où il n'en unique row idexiste pas ?

Ma table est

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

Je souhaite conserver les éléments suivants après la suppression des doublons:

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

J'ai essayé quelques requêtes mais je pense qu'elles dépendent d'un identifiant de ligne car je n'obtiens pas le résultat souhaité. Par exemple:

DELETE
FROM table
WHERE col1 IN (
    SELECT id
    FROM table
    GROUP BY id
    HAVING (COUNT(col1) > 1)
)
Fearghal
la source
5
Ce n'est pas une dupe du premier lien. Dans cette question, il n'y a pas d'ID de ligne et dans la question liée, il y a un ID de ligne. Très différent.
Alien Technology
changer 'SELECT id FROM table GROUP BY id HAVING' pour avoir une fonction agrégée par exemple MAX / MIN et cela devrait fonctionner.
foiré

Réponses:

786

J'aime les CTE et ROW_NUMBERcomme les deux combinés nous permettent de voir quelles lignes sont supprimées (ou mises à jour), il suffit donc de changer le DELETE FROM CTE...en 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 (le résultat est différent; je suppose que c'est dû à une faute de frappe de votre part)

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

Cet exemple détermine les doublons par une seule colonne en col1raison de la PARTITION BY col1. Si vous souhaitez inclure plusieurs colonnes, ajoutez-les simplement à PARTITION BY:

ROW_NUMBER()OVER(PARTITION BY Col1, Col2, ... ORDER BY OrderColumn)
Tim Schmelter
la source
2
Merci pour une excellente réponse. MSFT en revanche a une réponse très compliquée ici: stackoverflow.com/questions/18390574/…
Barka
2
@ omachu23: dans ce cas, cela n'a pas d'importance, même si je pense qu'il est plus efficace dans le CTE qu'à l'extérieur ( AND COl1='John'). Normalement, vous devez appliquer le filtre dans le CTE.
Tim Schmelter
1
@ omachu23: vous pouvez utiliser SQL dans le CTE ( en dehors de la commande), donc si vous voulez filtrer par Johns: ...FROM dbo.Table1 WHERE Col1='John'. Voici le violon: sqlfiddle.com/#!6/fae73/744/0
Tim Schmelter
1
La solution la plus simple est peut-être celle set rowcount 1 delete from t1 where col1=1 and col2=1que l'on voit ici
Zorgarath
15
Cette réponse supprimera uniquement les lignes contenant des doublons dans col1. Ajoutez les colonnes dans "select" à "partition by", par exemple en utilisant la sélection dans la réponse: RN = ROW_NUMBER () OVER (PARTITION BY col1, col2, col3, col4, col5, col6, col7 ORDER BY col1)
départ
159

Je préférerais CTE pour supprimer les lignes en double de la table du serveur SQL

recommande fortement de suivre cet article :: http://codaffection.com/sql-server-article/delete-duplicate-rows-in-sql-server/

en gardant l'original

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

sans garder l'original

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
la source
2
La fonction de fenêtrage est une excellente solution.
Robert Casey
2
Je suis un peu confus. Vous l'avez supprimé de CTE et non de la table d'origine. Alors, comment ça marche?
Bigeyes
8
@Bigeyes supprimant des enregistrements de CTE supprimera les enregistrements correspondants de la table physique réelle (car CTE contient une référence aux enregistrements réels).
Shamseer K
Je ne savais pas que c'était le cas jusqu'à ce poste ... Merci
Zakk Diaz
1
Pourquoi voudriez-vous supprimer à la fois l'original et son double? Je ne comprends pas pourquoi vous ne voudriez pas simplement supprimer le doublon et conserver l'autre.
Rich
52

Sans utiliser CTEet ROW_NUMBER()vous pouvez simplement supprimer les enregistrements en utilisant simplement group by avec la MAXfonction voici et exemple

DELETE
FROM MyDuplicateTable
WHERE ID NOT IN
(
SELECT MAX(ID)
FROM MyDuplicateTable
GROUP BY DuplicateColumn1, DuplicateColumn2, DuplicateColumn3)
Aamir
la source
4
Cette requête supprimera les enregistrements non dupliqués.
Derek Smalls du
8
Cela fonctionne bien, merci. @DerekSmalls cela ne supprime pas mes enregistrements non dupliqués.
monteirobrena
1
Ou vous pouvez conserver les enregistrements originaux en utilisantMIN(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
la source
Ne pourriez-vous pas réécrire à: où id dans (sélectionnez max (id) ... ayant count (*)> 1)?
Brent
1
Je ne pense pas qu'il soit nécessaire d'utiliser having ou union, cela suffira: supprimer de la recherche où id ne se trouve pas (sélectionner min (id) du groupe de recherche par URL)
Christopher Yang
9

Veuillez également consulter le mode de suppression ci-dessous.

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)

Créé un exemple de table nommée @tableet chargé avec les données fournies.

entrez la description de l'image ici

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

entrez la description de l'image ici

Remarque: Si vous donnez toutes les colonnes de la Partition bypièce, order bycela n'a pas beaucoup d'importance.

Je sais, la question a été posée il y a trois ans, et ma réponse est une autre version de ce que Tim a publié, mais publier juste au cas où cela serait utile pour n'importe qui.

Jithin Shaji
la source
9

Si vous n'avez pas de références, comme des clés étrangères, vous pouvez le faire. Je le fais beaucoup lors des tests de preuves de concept et les données de test sont dupliquées.

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

INTO [newTable]

Allez dans l'explorateur d'objets et supprimez l'ancienne table.

Renommez la nouvelle table avec le nom de l'ancienne table.

Rhys
la source
C'est le moyen le plus simple que j'ai appris dans mes documents d'introduction et que j'utilise.
Eric
7

Microsoft a un très bon guide sur la façon de supprimer les doublons. Consultez http://support.microsoft.com/kb/139444

En bref, voici le moyen le plus simple de supprimer les doublons lorsque vous n'avez que quelques lignes à supprimer:

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

myprimarykey est l'identifiant de la ligne.

Je règle le nombre de lignes sur 1 car je n'avais que deux lignes qui ont été dupliquées. Si j'avais eu 3 lignes dupliquées, j'aurais réglé le nombre de lignes sur 2 pour qu'il supprime les deux premières qu'il voit et n'en laisse qu'une dans le tableau t1.

J'espère que cela aide n'importe qui

oabarca
la source
1
Comment savoir combien de lignes j'ai dupliquées si j'ai 10 000 lignes?
Fearghal
@Fearghal essayez "sélectionnez primaryKey, count (*) dans myTable group by primaryKey;"
oabarca
1
Mais que faire s'il y a un nombre variable de lignes en double? c'est-à-dire que la ligne a a 2 enregistrements et la ligne b a 5 enregistrements et la ligne c n'a pas d'enregistrements en double
thermite
1
@ user2070775 Que se passe-t-il si seul un sous-ensemble de toutes les lignes contient des doublons, et parmi ces doublons, certains sont dupliqués deux fois et certains trois ou quatre fois?
thermite
@ user2070775 J'ai raté la partie où vous avez dit "juste quelques lignes à supprimer". Il y a également un avertissement sur la page concernant le nombre de lignes défini: dans les futures versions de sql, cela n'affectera pas les instructions de mise à jour ou de suppression
thermite
6

Essayez d'utiliser:

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

entrez la description de l'image ici

Fezal halai
la source
4

Après avoir essayé la solution suggérée ci-dessus, cela fonctionne pour les petites tables moyennes. Je peux suggérer cette solution pour de très grandes tables. car il s'exécute en itérations.

  1. Supprimer toutes les vues de dépendance du LargeSourceTable
  2. vous pouvez trouver les dépendances en utilisant sql managment studio, faites un clic droit sur la table et cliquez sur "Voir les dépendances"
  3. Renommez la table:
  4. sp_rename 'LargeSourceTable', 'LargeSourceTable_Temp'; GO
  5. Créez à LargeSourceTablenouveau, mais maintenant, ajoutez une clé primaire avec toutes les colonnes qui définissent les doublons ajouterWITH (IGNORE_DUP_KEY = ON)
  6. Par exemple:

    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. Créez à nouveau les vues que vous avez supprimées en premier lieu pour la nouvelle table créée

  8. Maintenant, exécutez le script sql suivant, vous verrez les résultats dans 1 000 000 lignes par page, vous pouvez changer le numéro de ligne par page pour voir les résultats plus souvent.

  9. Notez que j'ai activé IDENTITY_INSERTet désactivé car l'une des colonnes contient un identifiant incrémentiel automatique, que je copie également

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
la source
4

Il existe deux solutions mysql:

A) Supprimer les lignes en double à l'aide de l' DELETE JOINinstruction

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

Cette requête fait référence deux fois à la table des contacts. Par conséquent, elle utilise l'alias de table t1ett2 .

La sortie est:

1 requête OK, 4 lignes affectées (0,10 sec)

Dans le cas où vous souhaitez supprimer des lignes en double et conserver le lowest id, vous pouvez utiliser l'instruction suivante:

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

   

B) Supprimer les lignes en double à l'aide d'une table intermédiaire

Voici les étapes de suppression des lignes en double à l'aide d'une table intermédiaire:

    1. Créez un nouveau tableau avec la même structure que le tableau d'origine dont vous souhaitez supprimer les lignes en double.

    2. Insérez des lignes distinctes de la table d'origine dans la table immédiate.

    3. Insérez des lignes distinctes de la table d'origine dans la table immédiate.

 

Étape 1. Créez une nouvelle table dont la structure est la même que la table d'origine:

CREATE TABLE source_copy LIKE source;

Étape 2. Insérez des lignes distinctes de la table d'origine dans la nouvelle table:

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

Étape 3. déposez la table d'origine et renommez la table immédiate en celle d'origine

DROP TABLE source;
ALTER TABLE source_copy RENAME TO source;

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

Bashirpour
la source
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
la source
2

Vous devez regrouper les enregistrements en double en fonction du ou des champs, puis conserver l'un des enregistrements et supprimer le reste. Par exemple:

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
la source
2

La suppression des doublons d'une énorme table (plusieurs millions d'enregistrements) peut prendre du temps. Je vous suggère de faire une insertion en bloc dans une table temporaire des lignes sélectionnées plutôt que de supprimer.

--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
la source
2

Cela peut être fait de plusieurs façons dans le serveur SQL. La façon la plus simple de le faire est: d'insérer les lignes distinctes de la table des lignes en double dans la nouvelle table temporaire. Ensuite, supprimez toutes les données de la table des lignes en double, puis insérez toutes les données de la table temporaire qui n'a pas de doublons, comme indiqué ci-dessous.

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

   select * from table

Supprimer les lignes en double à l'aide de 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
la source
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
la source
1

En référence à https://support.microsoft.com/en-us/help/139444/how-to-remove-duplicate-rows-from-a-table-in-sql-server

L'idée de supprimer les doublons implique

  • a) Protéger les lignes qui ne sont pas en double
  • b) Conservez l'une des nombreuses lignes qualifiées ensemble comme doublons.

Pas à pas

  • 1) Identifiez d'abord les lignes qui satisfont à la définition de doublon et insérez-les dans la table temporaire, par exemple #tableAll.
  • 2) Sélectionnez des lignes non dupliquées (simples lignes) ou distinctes dans la table temporaire, par exemple #tableUnique.
  • 3) Supprimer de la table source joignant #tableAll pour supprimer les doublons.
  • 4) Insérez dans la table source toutes les lignes de #tableUnique.
  • 5) Supprimez #tableAll et #tableUnique
rajibdotnet
la source
1

Si vous avez la possibilité d'ajouter temporairement une colonne à la table, c'était une solution qui a fonctionné pour moi:

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

Effectuez ensuite une SUPPRESSION en utilisant une combinaison de MIN et 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
                 );

Vérifiez que la suppression a été effectuée correctement:

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 

Le résultat ne doit avoir aucune ligne avec un nombre supérieur à 1. Enfin, supprimez la colonne rowid:

ALTER TABLE dbo.DUPPEDTABLE DROP COLUMN RowID;
j.hull
la source
0

Une autre façon de supprimer des lignes dupliquées sans perdre d'informations en une seule étape est la suivante:

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
la source
0

Oh wow, je me sens tellement stupide en prêtant toutes ces réponses, elles sont comme la réponse des experts avec toutes les tables CTE et temp et etc.

Et tout ce que j'ai fait pour le faire fonctionner, c'est simplement agréger la colonne ID en utilisant MAX.

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

REMARQUE: vous devrez peut-être l'exécuter plusieurs fois pour supprimer les doublons, car cela ne supprimera qu'un seul ensemble de lignes en double à la fois.

foiré
la source
Cela ne fonctionnera pas car il supprimera tous les doublons sans laisser les originaux. OP demande de conserver les enregistrements originaux.
0xdd
2
Ce n'est pas vrai, max vous donnera un ID maximum qui satisfait à la condition. Si ce n'est pas vrai, prouvez votre cas pour un vote négatif.
foiré le
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
la source
La suppression des doublons d'une énorme table (plusieurs millions d'enregistrements) peut prendre du temps. Je vous suggère de faire une insertion en bloc dans une table temporaire des lignes sélectionnées plutôt que de les supprimer. '- RÉÉCRITURE DE VOTRE CODE (PRENEZ NOTE DE LA 3ÈME LIGNE) AVEC 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
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
la source