Question

Quelle est la meilleure façon de supprimer les lignes en double d'un fichier assez volumineux SQL Server tableau (c'est-à-dire300 000+ lignes) ?

Bien entendu, les lignes ne seront pas des doublons parfaits en raison de l'existence du RowID champ d’identité.

Ma table

RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null
Était-ce utile?

La solution

En supposant qu'il n'y ait pas de valeurs nulles, vous GROUP BY les colonnes uniques, et SELECT le MIN (or MAX) RowId comme ligne à conserver.Ensuite, supprimez simplement tout ce qui n’a pas d’identifiant de ligne :

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

Si vous avez un GUID au lieu d'un entier, vous pouvez remplacer

MIN(RowId)

avec

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

Autres conseils

Une autre façon possible de procéder est

; 

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

j'utilise ORDER BY (SELECT 0) ci-dessus car la ligne à conserver en cas d'égalité est arbitraire.

Pour conserver le dernier dans RowID commande par exemple, vous pouvez utiliser ORDER BY RowID DESC

Plans d'exécution

Le plan d'exécution pour cela est souvent plus simple et plus efficace que celui de la réponse acceptée car il ne nécessite pas d'auto-jointure.

Execution Plans

Ce n’est cependant pas toujours le cas.Un endroit où le GROUP BY Une solution pourrait être préférée dans les situations où un agrégat de hachage serait choisi de préférence à un agrégat de flux.

Le ROW_NUMBER solution donnera toujours à peu près le même plan alors que le GROUP BY la stratégie est plus flexible.

Execution Plans

Les facteurs qui pourraient favoriser l'approche globale de hachage seraient

  • Aucun index utile sur les colonnes de partitionnement
  • relativement moins de groupes avec relativement plus de doublons dans chaque groupe

Dans les versions extrêmes de ce deuxième cas (s'il y a très peu de groupes avec de nombreux doublons dans chacun), on pourrait aussi envisager simplement d'insérer les lignes à conserver dans une nouvelle table puis TRUNCATE-ing l'original et les recopier pour minimiser la journalisation par rapport à la suppression d'une très forte proportion de lignes.

Il y a un bon article sur supprimer les doublons sur le site de support Microsoft.C'est assez conservateur - ils vous demandent de tout faire en étapes séparées - mais cela devrait bien fonctionner contre de grandes tables.

J'ai utilisé des auto-jointures pour faire cela dans le passé, même si cela pourrait probablement être agrémenté d'une clause HAVING :

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

La requête suivante est utile pour supprimer les lignes en double.Le tableau de cet exemple a ID comme colonne d'identité et les colonnes qui ont des données en double sont Column1, Column2 et 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) 

Le script suivant montre l'utilisation de GROUP BY, HAVING, ORDER BY dans une requête et renvoie les résultats avec la colonne en double et son nombre.

SELECT YourColumnName,
       COUNT(*) TotalCount
FROM   YourTableName
GROUP  BY YourColumnName
HAVING COUNT(*) > 1
ORDER  BY COUNT(*) DESC 
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
DELETE LU 
FROM   (SELECT *, 
               Row_number() 
                 OVER ( 
                   partition BY col1, col1, col3 
                   ORDER BY rowid DESC) [Row] 
        FROM   mytable) LU 
WHERE  [row] > 1 

Cela supprimera les lignes en double, à l'exception de la première ligne

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

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

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

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

Quick and Dirty pour supprimer exactement les lignes dupliquées (pour les petites tables) :

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

Je préfère la solution subquery\having count(*) > 1 à la jointure interne car je l'ai trouvée plus facile à lire et il était très facile de la transformer en une instruction SELECT pour vérifier ce qui serait supprimé avant de l'exécuter.

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

Pour récupérer les lignes en double :

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

Pour supprimer les lignes en double :

DELETE users 
WHERE rowid NOT IN 
(SELECT MIN(rowid)
FROM users
GROUP BY name, email);      
SELECT  DISTINCT *
      INTO tempdb.dbo.tmpTable
FROM myTable

TRUNCATE TABLE myTable
INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable
DROP TABLE tempdb.dbo.tmpTable

J'ai pensé partager ma solution car elle fonctionne dans des circonstances particulières.Dans mon cas, la table avec des valeurs en double n'avait pas de clé étrangère (car les valeurs étaient dupliquées à partir d'une autre base de données).

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 :lorsque je travaille sur des choses comme celle-ci, j'utilise toujours une transaction, cela garantit non seulement que tout est exécuté dans son ensemble, mais me permet également de tester sans rien risquer.Mais bien sûr, vous devriez quand même faire une sauvegarde juste pour être sûr...

Utilisation du CTE.L'idée est de joindre une ou plusieurs colonnes qui forment un enregistrement en double, puis de supprimer celle de votre choix :

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

Une autre solution simple peut être trouvée sur le lien collé ici.Celui-ci est facile à comprendre et semble efficace pour la plupart des problèmes similaires.C'est pour SQL Server mais le concept utilisé est plus qu'acceptable.

Voici les parties pertinentes de la page liée :

Considérez ces données :

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

Alors, comment pouvons-nous supprimer ces données en double ?

Tout d’abord, insérez une colonne d’identité dans cette table en utilisant le code suivant :

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

Utilisez le code suivant pour le résoudre :

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

Cette requête a montré de très bonnes performances pour moi :

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

il a supprimé 1 million de lignes en un peu plus de 30 secondes sur une table de 2 millions (50 % de doublons)

Voici un autre bon article sur supprimer les doublons.

Il explique pourquoi c'est difficile :"SQL est basé sur l'algèbre relationnelle et les doublons ne peuvent pas se produire dans l'algèbre relationnelle, car les doublons ne sont pas autorisés dans un ensemble."

La solution de table temporaire et deux exemples MySQL.

À l’avenir, allez-vous l’empêcher au niveau de la base de données ou du point de vue de l’application.Je suggérerais le niveau de la base de données car votre base de données devrait être responsable du maintien de l'intégrité référentielle, les développeurs ne feront que causer des problèmes ;)

Oh, bien sûr.Utilisez une table temporaire.Si vous voulez une instruction unique, peu performante, qui « fonctionne », vous pouvez utiliser :

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)

Fondamentalement, pour chaque ligne du tableau, la sous-sélection recherche le RowID supérieur de toutes les lignes qui ressemblent exactement à la ligne considérée.Vous vous retrouvez donc avec une liste de RowID qui représentent les lignes « originales » non dupliquées.

J'avais une table dans laquelle je devais conserver les lignes non dupliquées.Je ne suis pas sûr de la vitesse ou de l'efficacité.

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

L'autre façon est Créer un nouveau table avec les mêmes champs et avec index unique.Alors déplacer toutes les données de l'ancienne table vers la nouvelle table.Ignorer automatiquement SQL SERVER (il existe également une option sur ce qu'il faut faire s'il y a une valeur en double :ignorer, interrompre ou qch) valeurs en double.Nous avons donc le même tableau sans lignes en double. Si vous ne voulez pas d'index unique, après le transfert des données, vous pouvez le supprimer.

En particulier pour les grandes tables vous pouvez utiliser DTS (package SSIS pour importer/exporter des données) afin de transférer rapidement toutes les données vers votre nouvelle table indexée de manière unique.Pour 7 millions de lignes, cela ne prend que quelques minutes.

Utilisez ceci

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

En utilisant la requête ci-dessous, nous pouvons supprimer les enregistrements en double en fonction d'une seule ou de plusieurs colonnes.la requête ci-dessous est supprimée sur la base de deux colonnes.le nom de la table est : testing et les noms de colonnes empno,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)
  1. Créer un nouveau tableau vierge avec la même structure

  2. Exécuter une requête comme celle-ci

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) > 1
    
  3. Exécutez ensuite cette requête

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

C'est le moyen le plus simple de supprimer un enregistrement en double

 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

Je mentionnerais cette approche car elle peut être utile et fonctionne sur tous les serveurs SQL :Très souvent, il n’y a qu’un ou deux doublons, et les identifiants et le nombre de doublons sont connus.Dans ce cas:

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

Du niveau application (malheureusement).Je conviens que la bonne façon d'éviter la duplication est au niveau de la base de données grâce à l'utilisation d'un index unique, mais dans SQL Server 2005, un index ne peut contenir que 900 octets, et mon champ varchar (2048) supprime cela.

Je ne sais pas dans quelle mesure cela fonctionnerait, mais je pense que vous pourriez écrire un déclencheur pour appliquer cela, même si vous ne pouviez pas le faire directement avec un index.Quelque chose comme:

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

De plus, varchar(2048) me semble louche (certaines choses dans la vie font 2048 octets, mais c'est assez rare) ;ça ne devrait vraiment pas être varchar(max) ?

DELETE
FROM
    table_name T1
WHERE
    rowid > (
        SELECT
            min(rowid)
        FROM
            table_name T2
        WHERE
            T1.column_name = T2.column_name
    );
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)

Si vous souhaitez prévisualiser les lignes que vous êtes sur le point de supprimer et garder le contrôle sur les lignes en double à conserver.Voir 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
DELETE 
FROM MyTable
WHERE NOT EXISTS (
              SELECT min(RowID)
              FROM Mytable
              WHERE (SELECT RowID 
                     FROM Mytable
                     GROUP BY Col1, Col2, Col3
                     ))
               );
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top