245 votes

Recherche de lignes dupliquées dans SQL Server

J'ai une base de données SQL Server contenant des organisations, et il y a beaucoup de lignes en double. Je veux exécuter une instruction de sélection pour récupérer toutes ces lignes et le nombre de doublons, mais aussi renvoyer les identifiants associés à chaque organisation.

Une déclaration comme :

SELECT     orgName, COUNT(*) AS dupes  
FROM         organizations  
GROUP BY orgName  
HAVING      (COUNT(*) > 1)

Il retournera quelque chose comme

orgName        | dupes  
ABC Corp       | 7  
Foo Federation | 5  
Widget Company | 2 

Mais j'aimerais aussi récupérer leurs identifiants. Existe-t-il un moyen de le faire ? Peut-être comme un

orgName        | dupeCount | id  
ABC Corp       | 1         | 34  
ABC Corp       | 2         | 5  
...  
Widget Company | 1         | 10  
Widget Company | 2         | 2  

La raison en est qu'il existe également une table séparée des utilisateurs qui sont liés à ces organisations, et je voudrais les unifier (donc supprimer les doublons pour que les utilisateurs soient liés à la même organisation et non à des organisations doubles). J'aimerais que cette opération se fasse manuellement pour ne rien gâcher, mais j'aurais quand même besoin d'une déclaration renvoyant les ID de toutes les organisations dupliquées afin de pouvoir parcourir la liste des utilisateurs.

328voto

RedFilter Points 84190
select o.orgName, oc.dupeCount, o.id
from organizations o
inner join (
    SELECT orgName, COUNT(*) AS dupeCount
    FROM organizations
    GROUP BY orgName
    HAVING COUNT(*) > 1
) oc on o.orgName = oc.orgName

5 votes

Y a-t-il des limites à cette requête, par exemple si le nombre d'enregistrements est supérieur à 10 millions ?

5 votes

@Steam Vous avez raison : cette réponse n'est pas efficace dans une grande base de données avec des millions d'enregistrements. Préférez la réponse GroupBy/Having soumise par Aykut, qui peut être mieux optimisée par la base de données. Une exception : Je suggère d'utiliser Count(0) au lieu de Count(*), pour simplifier les choses.

1 votes

@Mike - pourquoi Count(0) vs Count(*) ?

96voto

Aykut Points 227

Vous pouvez exécuter la requête suivante et trouver les doublons avec max(id) et supprimer ces lignes.

SELECT orgName, COUNT(*), Max(ID) AS dupes 
FROM organizations 
GROUP BY orgName 
HAVING (COUNT(*) > 1)

Mais vous devrez exécuter cette requête plusieurs fois.

0 votes

Vous devez l'exécuter exactement MAX( COUNT(*) ) - 1 temps, ce qui pourrait encore être faisable.

2 votes

Bonjour, y a-t-il un moyen d'obtenir tous les identifiants au lieu de l'identifiant maximum, par exemple pour 2, je peux utiliser max et min, mais qu'en est-il pour plus de 2 ? @DerMike

32voto

Paul Points 3876

Vous pouvez le faire comme ça :

SELECT
    o.id, o.orgName, d.intCount
FROM (
     SELECT orgName, COUNT(*) as intCount
     FROM organizations
     GROUP BY orgName
     HAVING COUNT(*) > 1
) AS d
    INNER JOIN organizations o ON o.orgName = d.orgName

Si vous voulez retourner uniquement les enregistrements qui peuvent être supprimés (en laissant un de chaque), vous pouvez utiliser :

SELECT
    id, orgName
FROM (
     SELECT 
         orgName, id,
         ROW_NUMBER() OVER (PARTITION BY orgName ORDER BY id) AS intRow
     FROM organizations
) AS d
WHERE intRow != 1

Edit : SQL Server 2000 ne dispose pas de la fonction ROW_NUMBER(). A la place, vous pouvez utiliser :

SELECT
    o.id, o.orgName, d.intCount
FROM (
     SELECT orgName, COUNT(*) as intCount, MIN(id) AS minId
     FROM organizations
     GROUP BY orgName
     HAVING COUNT(*) > 1
) AS d
    INNER JOIN organizations o ON o.orgName = d.orgName
WHERE d.minId != o.id

0 votes

La première déclaration fonctionne, mais la seconde ne semble pas fonctionner.

0 votes

Le serveur SQL ne semble pas être capable de reconnaître row_number() ?

0 votes

Ah...avez-vous une ancienne version de SQL Server ? Je crois que cela a été introduit dans SQL Server 2005.

10voto

Arif Ansari Points 223

Vous pouvez essayer ceci, c'est le mieux pour vous.

 WITH CTE AS
    (
    SELECT *,RN=ROW_NUMBER() OVER (PARTITION BY orgName ORDER BY orgName DESC) FROM organizations 
    )
    select * from CTE where RN>1
    go

0 votes

Comment obtenir tous les identifiants dans des colonnes séparées par des virgules ou différentes ?

9voto

ecairol Points 211

La solution indiquée comme correcte n'a pas fonctionné pour moi, mais j'ai trouvé cette réponse qui fonctionne parfaitement : Obtenir la liste des lignes en double dans MySql

SELECT n1.* 
FROM myTable n1
INNER JOIN myTable n2 
ON n2.repeatedCol = n1.repeatedCol
WHERE n1.id <> n2.id

0 votes

Vous obtiendrez beaucoup de doublons dans l'ensemble des résultats, et vous devrez donc les gérer également.

1 votes

Si l'identifiant est numérique, la vérification n1.id > n2.id empêchera chaque paire d'apparaître deux fois.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X