Comment supprimer des lignes en double?

1285

Quelle est la meilleure façon de supprimer les lignes en double d'une SQL Servertable assez grande (c'est-à-dire plus de 300 000 lignes)?

Les lignes, bien sûr, ne seront pas des doublons parfaits en raison de l'existence du RowIDchamp 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
Seibar
la source
13
Petit conseil pour les utilisateurs de PostgreSQL qui lisent ceci (beaucoup, en fonction de la fréquence à laquelle il est lié): Pg n'expose pas les termes CTE en tant que vues pouvant être mises à jour, vous ne pouvez donc pas DELETE FROMdirectement utiliser un terme CTE. Voir stackoverflow.com/q/18439054/398670
Craig Ringer
@CraigRinger la même chose est vraie pour Sybase - J'ai collecté les solutions restantes ici (devrait être valide pour PG et d'autres aussi: stackoverflow.com/q/19544489/1855801 (remplacez simplement la ROWID()fonction par la colonne RowID, le cas échéant)
maf-soft
12
Juste pour ajouter une mise en garde ici. Lorsque vous exécutez un processus de déduplication, vérifiez toujours ce que vous supprimez en premier! C'est l'un de ces domaines où il est très courant de supprimer accidentellement de bonnes données.
Jeff Davis

Réponses:

1142

En supposant qu'aucune valeur nulle, vous GROUP BYles colonnes uniques et SELECTle MIN (or MAX)RowId comme ligne à conserver. Ensuite, supprimez tout ce qui n'a pas d'ID 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)))
Mark Brackett
la source
327
Cela fonctionnerait-il aussi? DELETE FROM MyTable WHERE RowId NOT IN (SELECT MIN(RowId) FROM MyTable GROUP BY Col1, Col2, Col3);
Georg Schölly
10
@Andriy - Dans SQL Server LEFT JOINest moins efficace que NOT EXISTS sqlinthewild.co.za/index.php/2010/03/23/... Le même site compare également NOT INvs NOT EXISTS. sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in Sur les 3, je pense que les NOT EXISTSmeilleurs résultats. Tous les trois généreront un plan avec une auto-jointure, mais cela peut être évité.
Martin Smith
12
@Martin, @Georg: Donc, j'ai fait un petit test. Un grand tableau a été créé et rempli comme décrit ici: sqlinthewild.co.za/index.php/2010/03/23/… Deux SELECT ont ensuite été produits, l'un en utilisant la technique LEFT JOIN + WHERE IS NULL, l'autre en utilisant le NOT Dans une. Ensuite, j'ai poursuivi les plans d'exécution, et devinez quoi? Les coûts de requête étaient de 18% pour LEFT JOIN contre 82% pour NOT IN, une grande surprise pour moi. J'aurais peut-être fait quelque chose que je n'aurais pas dû faire ou vice versa, ce que, si c'est vrai, j'aimerais vraiment savoir.
Andriy M
16
@ GeorgSchölly a fourni une réponse élégante. Je l'ai utilisé sur une table où un bug PHP a créé des lignes en double.
Philip Kearns
12
Désolé mais pourquoi DELETE MyTable FROM MyTablela syntaxe est-elle correcte? Je ne vois pas de mettre le nom de la table juste après l' DELETEoption comme option dans la documentation ici . Désolé si cela est évident pour les autres; Je suis un débutant en SQL essayant juste d'apprendre. Plus important que pourquoi cela fonctionne-t-il: quelle est la différence entre y inclure ou non le nom de la table?
levininja
760

Une autre façon possible de le faire 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 ci- ORDER BY (SELECT 0)dessus car il est arbitraire quelle ligne conserver en cas d'égalité.

Pour conserver la dernière version RowIDafin, par exemple, vous pouvez utiliserORDER 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 l'auto-jointure.

Plans d'exécution

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

La ROW_NUMBERsolution donnera toujours à peu près le même plan alors que la GROUP BYstratégie est plus flexible.

Plans d'exécution

Les facteurs qui pourraient favoriser l'approche globale du 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 également envisager d'insérer simplement les lignes à conserver dans une nouvelle table, puis de TRUNCATEcopier l'original et de les recopier pour minimiser la journalisation par rapport à la suppression d'un très grande proportion de rangées.

Martin Smith
la source
28
Si je peux ajouter: La réponse acceptée ne fonctionne pas avec les tableaux qui utilisent uniqueidentifier. Celui-ci est beaucoup plus simple et fonctionne parfaitement sur n'importe quelle table. Merci Martin.
BrunoLM
15
C'est une réponse tellement géniale! Cela a fonctionné quand j'ai retiré l'ancien PK avant de réaliser où il y avait des doublons. +100
Mikael Eliasson
12
Je suggère de poser puis de répondre à cette question (avec cette réponse) sur DBA.SE. Ensuite, nous pouvons l'ajouter à notre liste de réponses canoniques .
Nick Chammas
16
Contrairement à la réponse acceptée, cela a également fonctionné sur une table qui n'avait pas de clé ( RowId) pour comparer.
vossad01
8
En revanche, celui-ci ne fonctionne pas sur toutes les versions de SQL Server
David
150

Il y a un bon article sur la suppression des doublons sur le site de support Microsoft. C'est assez conservateur - ils vous font tout faire dans des étapes distinctes - mais cela devrait bien fonctionner contre de grandes tables.

J'ai utilisé des auto-jointures pour le faire 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
Jon Galloway
la source
parfait! j'ai trouvé que c'était le moyen le plus efficace pour supprimer les lignes en double sur mon ancienne version de mariadb 10.1.xx. Merci!
Drunken M
Beaucoup plus simple et plus facile à comprendre!
Marc
98

La requête suivante est utile pour supprimer des lignes en double. La table de cet exemple a IDune colonne d'identité et les colonnes contenant des données en double sont Column1, Column2et 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 BYdans une requête, et renvoie les résultats avec colonne en double et son compte.

SELECT YourColumnName,
       COUNT(*) TotalCount
FROM   YourTableName
GROUP  BY YourColumnName
HAVING COUNT(*) > 1
ORDER  BY COUNT(*) DESC 
gngolakia
la source
1
Erreur MySQL avec le premier script 'Vous ne pouvez pas spécifier la table cible' TableName 'pour la mise à jour dans la clause FROM'
D.Rosado
Hormis l'erreur D.Rosado déjà signalée, votre première requête est également très lente. La requête SELECT correspondante a pris ma configuration + - 20 fois plus longtemps que la réponse acceptée.
parvus
8
@parvus - La question est balisée SQL Server et non MySQL. La syntaxe est correcte dans SQL Server. MySQL est également notoirement mauvais pour optimiser les sous-requêtes, voir par exemple ici . Cette réponse est correcte dans SQL Server. En fait, NOT INil fonctionne souvent mieux que OUTER JOIN ... NULL. J'ajouterais un HAVING MAX(ID) IS NOT NULLà la requête bien que même sémantiquement cela ne devrait pas être nécessaire car cela peut améliorer l' exemple de
Martin Smith
2
Fonctionne très bien dans PostgreSQL 8.4.
vers
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
la source
Pourquoi publier une solution Postgres sur une question SQL Server?
Lankymart
2
@Lankymart Parce que les utilisateurs postgres viennent aussi ici. Regardez le score de cette réponse.
Gabriel
2
J'ai vu cela dans certaines questions SQL populaires, comme ici , ici et ici . Le PO a obtenu sa réponse et tout le monde a également obtenu de l'aide. Pas de problème à mon humble avis.
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
la source
1
J'obtiens ce message sur azur SQL DW: une clause FROM n'est actuellement pas prise en charge dans une instruction DELETE.
Amit
40

Cela supprimera les lignes en double, sauf la première ligne

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

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

Syed Mohamed
la source
10
Pour mysql, il donnera l'erreur: Code d'erreur: 1093. Vous ne pouvez pas spécifier la table cible 'Mytable' pour la mise à jour dans la clause FROM. mais ce petit changement fonctionnera pour mysql: DELETE FROM Mytable WHERE RowID NOT IN (SELECT ID FROM (SELECT MIN (RowID) AS ID FROM Mytable GROUP BY Col1, Col2, Col3) AS TEMP)
Ritesh
35

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
24

Pour récupérer des 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);      
Shaini Sinha
la source
Pour les utilisateurs de MySQL, notez tout d'abord que cela doit être DELETE FROM, deuxièmement, cela ne fonctionnera pas, car vous ne pouvez pas à SELECTpartir de la même table que vous utilisez DELETE. Dans MySQL, cela décolle MySQL error 1093.
Íhor Mé
23

Rapide et sale pour supprimer les lignes dupliquées exactes (pour les petites tables):

select  distinct * into t2 from t1;
delete from t1;
insert into t1 select *  from t2;
drop table t2;
JuanJo
la source
3
Notez que la question spécifie en fait une duplication non exacte (id de ligne dueto).
Dennis Jaheruddin
21

Je préfère la sous-requête \ ayant la solution count (*)> 1 à la jointure interne car je l'ai trouvée plus facile à lire et il était très facile de se 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
--)
James Errico
la source
Ne supprime-t-il pas tous les enregistrements qui apparaissent dans la requête interne. Nous devons supprimer uniquement les doublons et conserver l'original.
Sandy
3
Vous ne renvoyez que celui dont l'ID est le plus bas, en fonction du min (id) dans la clause select.
James Errico
2
Décommentez les première, deuxième et dernière lignes de la requête.
James Errico
7
Cela ne nettoiera pas tous les doublons. Si vous avez 3 lignes en double, il ne sélectionnera que la ligne avec le MIN (id) et supprimera celle-ci, en laissant deux lignes à gauche qui sont des doublons.
Chloe
2
Néanmoins, j'ai fini par utiliser cette déclaration répétée encore et encore, afin qu'elle progresse réellement au lieu de suspendre la connexion ou de mettre l'ordinateur en veille. Je l'ai changé MAX(id)pour éliminer les derniers doublons et ajouté LIMIT 1000000à la requête interne afin qu'il n'ait pas à analyser toute la table. Cela a montré des progrès beaucoup plus rapides que les autres réponses, qui semblaient se bloquer pendant des heures. Une fois que la table a été élaguée à une taille gérable, vous pouvez terminer avec les autres requêtes. Astuce: assurez-vous que col1 / col2 / col3 possède des indices de regroupement par.
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
la source
5
La troncature ne fonctionnera pas si vous avez des références de clé étrangère à myTable.
Sameer Alibhai
15

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 ont été 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 ça, j'utilise toujours une transaction, cela garantit non seulement que tout est exécuté dans son ensemble, mais me permet également de tester sans risquer quoi que ce soit. Mais bien sûr, vous devriez quand même faire une sauvegarde juste pour être sûr ...

Ruben Verschueren
la source
14

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é 1M lignes en un peu plus de 30sec d'une table de 2M (50% de doublons)

Draško
la source
14

Utilisation de CTE. L'idée est de joindre sur une ou plusieurs colonnes qui forment un enregistrement en double, puis de supprimer celui que vous aimez:

;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
la source
1
Je pense que vous manquez un ET dans votre JOIN.
Justin R.
13

Encore une autre solution simple peut être trouvée sur le lien collé ici . Celui-ci est facile à saisir et semble être 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) 
Nitish Pareek
la source
1
"Facile à saisir", "semble être efficace", mais pas un mot sur ce en quoi consiste la méthode. Imaginez simplement que le lien devienne invalide, à quoi servirait alors de savoir que la méthode était facile à saisir et efficace? Veuillez envisager d'ajouter des parties essentielles de la description de la méthode dans votre message, sinon ce n'est pas une réponse.
Andriy M
Cette méthode est utile pour les tables où vous n'avez pas encore défini d'identité. Souvent, vous devez vous débarrasser des doublons pour définir la clé primaire!
Jeff Davis
@JeffDavis - La ROW_NUMBERversion fonctionne très bien dans ce cas sans avoir à aller jusqu'à ajouter une nouvelle colonne avant de commencer.
Martin Smith
12

Voici un autre bon article sur la suppression des doublons .

Il explique pourquoi il 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 causeront juste des problèmes;)

Craig
la source
1
SQL est basé sur plusieurs ensembles. Mais même s'il était basé sur des ensembles, ces deux tuples (1, a) et (2, a) sont différents.
Andrew
12

Oh, bien sûr. Utilisez une table temporaire. Si vous voulez une seule déclaration peu performante qui "fonctionne", vous pouvez aller avec:

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 trouve le RowID supérieur de toutes les lignes qui sont exactement comme la ligne considérée. Vous vous retrouvez donc avec une liste de RowID qui représentent les lignes "d'origine" non dupliquées.

Jacob Proffitt
la source
11

J'avais une table où j'avais besoin de 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 )
chrismar035
la source
7
Cela suppose qu'il y a au plus 1 doublon.
Martin Smith
Pourquoi ne pas HAVING COUNT(*) > 1?
Philipp M
11

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
Haris
la source
10

L'autre méthode consiste à créer une nouvelle table avec les mêmes champs et avec un index unique . Déplacez ensuite toutes les données de l'ancienne table vers la nouvelle table . SQL SERVER ignore automatiquement (il y a aussi une option sur ce qu'il faut faire s'il y aura une valeur en double: ignorer, interrompre ou sth) les valeurs en double. Nous avons donc la même table sans lignes en double. Si vous ne voulez pas d'index unique, après le transfert des données, vous pouvez le supprimer .

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

İsmail Yavuz
la source
9

En utilisant la requête ci-dessous, nous pouvons supprimer les enregistrements en double sur la base d'une seule colonne ou de plusieurs colonnes. la requête ci-dessous est en train de supprimer sur la base de deux colonnes. le nom de la table est: testinget les noms des colonnesempno,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
la source
9
  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
shA.t
la source
9

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

Harikesh Yadav
la source
Pourquoi quelqu'un vote-t-il cela? Si vous avez plus de deux identifiants identiques, cela ne fonctionnera PAS. Au lieu de cela, écrivez: supprimer de tblemp où l'id n'est pas dans (sélectionnez min (id) du groupe tblemp par titre)
crellee
7

Je mentionnerais cette approche aussi bien qu'elle peut être utile et fonctionne dans tous les serveurs SQL: assez 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
Evgueny Sedov
la source
7

Depuis le niveau de l'application (malheureusement). Je conviens que la bonne façon d'empêcher 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) le supprime.

Je ne sais pas à quel point cela fonctionnerait, mais je pense que vous pouvez écrire un déclencheur pour appliquer cela, même si vous ne pouvez 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); cela ne devrait-il vraiment pas être varchar (max)?

DrPizza
la source
7

Une autre façon de le faire: -

DELETE A
FROM   TABLE A,
       TABLE B
WHERE  A.COL1 = B.COL1
       AND A.COL2 = B.COL2
       AND A.UNIQUEFIELD > B.UNIQUEFIELD 
yuvi
la source
Quelle est la différence avec cette réponse existante du 20 août 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
    );
Teena
la source
Salut Teena, vous avez manqué la table Alice nom T1 après le commentaire de suppression, sinon cela entraînera une exception de syntaxe.
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
la source
6

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
Lauri Lubi
la source