2009-08-26 4 views
2

J'ai essayé de supprimer les enregistrements en double dans ma base de données. La seule différence est la PrimaryKey qui est un identifiant unique. J'ai environ 1500 entrées qui ont été dupées donc dans tout ce que je regarde environ 3000 entrées. J'ai donc sectionné environ 60 entrées (basé sur la date receivedOn) et exécuté mon code pour les réduire à 30 et OH CRAP le 30 a disparu! Voici le code que j'ai essayé:Essayer de supprimer les entrées en double dans la base de données SQL a supprimé tous les enregistrements. Qu'est ce qui ne s'est pas bien passé?

DELETE dupes 
FROM [emailTable] dupes, [emailTable] fullTable 
WHERE (dupes.ReceivedOn > '2009-08-18 23:59:59.999' AND dupes.ReceivedOn < '2009-08-20 00:00:00.000') 
     AND (dupes.emlPath = fullTable.emlPath) 
     AND NOT (dupes.GUID = fullTable.GUID) 

Mon but est de supprimer le doublon. Je ne m'inquiète pas lequel ... mais j'ai besoin d'une des deux entrées pour rester sur le serveur ... Quelqu'un peut-il faire la lumière sur ce que j'ai fait de mal?

+1

Peut-être que vous faites déjà, mais je ne voudrais pas supprimer tout ou modifier les données de production de quelque manière que sans vérifier d'abord la requête fonctionne dans une transaction avec un ROLLBACK. Quand vous savez que vous avez raison, changez ROLLBACK pour COMMIT. –

+0

Je ne suis pas! et cela serait probablement utile. Comment puis-je faire cela? – swolff1978

+0

BEGIN TRAN * votre code ici * ROLLBACK TRAN COMMIT TRAN lorsque les lignes affectées ressemblent au numéro attendu, mettez en surbrillance COMMIT TRAN et exécutez-le. –

Répondre

7

Vous pouvez le faire sans deuxième table. Quelque chose comme ceci:

SELECT * FROM emailTable 
WHERE EXISTS (
    SELECT * FROM emailTable AS t2 
    WHERE t2.emlPath = emailTable.emlPath AND 
    t2.GUID > emailTable.GUID) 

Cela vous montrera quels enregistrements sont sur le point d'être supprimés. Si ça va, changer pour:

DELETE FROM emailTable 
WHERE EXISTS (
    SELECT * FROM emailTable AS t2 
    WHERE t2.emlPath = emailTable.emlPath AND 
    t2.GUID > emailTable.GUID) 

Le t2.GUID > emailTable.GUID fera en sorte qu'un enregistrement avec cette emlPath restera dans le tableau.

2

Vous devez effectuer la suppression avec une sous-sélection, pas une jointure. L'avantage de cette façon est que vous pouvez prévisualiser les GUID que vous supprimerez avant de les supprimer réellement. (Il suffit d'exécuter la requête de sélection par elle-même)

Ce outta faire, il supprimera le plus petit GUID

delete from emailTable where GUID in 
    (

    select MIN(dupe.GIUD) from emailTable dupe 
     INNER JOIN emailTable noDupe 
     ON dupe.emlPath=noDupe.emlPath 
      where recievedOn between '2009-8-18' and '2009-8-20' 
       GROUP BY dupe.emlPath 
    ) 
1

Ce que vous avez fait le mal est que votre requête n'exclut pas des doublons. Il sélectionne les doublons qui sont différents d'un autre doublon avec le même chemin, mais chaque doublon est différent d'un autre doublon.

Qu'est-ce que vous avez à faire est d'abord choisir les doublons que vous souhaitez conserver, par exemple:

select min(GUID) 
from emailTable 
where ReceivedOn > '...' and ReceivedOn < '...' 
group by emlPath 
having count(*) > 1 

Ensuite, vous supprimez tous les doublons, sauf ceux-ci.

+0

J'allais suggérer d'utiliser RANK, et rejoindre sur les ids qui correspondent & le rang est 2. –

+0

@rexem, méfiez-vous d'avoir un lien dans les valeurs étant classés. Utilisez ROW_NUMBER() à la place. – JeffO

0

Vous ne devez pas utiliser "=" dans votre jointure. ie "AND NOT (dupes.GUID = fullTable.GUID)" Cette condition ne fera rien car le GUID de vos lignes dupliquées doit être différent.

Vous devez utiliser supérieur à. à savoir

delete from emailTable 
WHERE EXISTS 
(
    SELECT ID FROM emailTable t2 
    WHERE emailTable.GUID > t2.GUID 
    AND emailTable.emlPath= t2.emlPath 
) 
0

je préfère utiliser une expression de table commune pour cela et ROW_NUMBER():

with cte as (
    select row_number() over (partition by emlPath order by GUID) as eml_no 
     , ReceivedOn 
    from [emailTables]) 
delete from cte 
    where eml_no > 1 
    and ReceivedOn between '2009-08-18 23:59:59.999' AND '2009-08-20 00:00:00.000'; 

Je preffer parce qu'il donne un contrôle stirct sur lequel en double ligne est supprimée. Je peux supprimer le troisième et en garder deux, je peux choisir n'importe quel numéro de commande que je veux garder le premier, et il traite très bien avec des liens.

0

C'était le code que j'ai fini à grâce à l'aide de tous les postes:

DELETE A 
    FROM [emailTable] A, [emailTable] B 
    WHERE A.MessageID = B.MessageID 
     AND A.GUID > B.GUID 
Questions connexes