Recherche de lignes en double dans SQL Server


231

J'ai une base de données d'organisations SQL Server et il existe de nombreuses lignes en double. Je veux exécuter une instruction select pour saisir tout cela et la quantité de dupes, 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)

Rendra quelque chose comme

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

Mais j'aimerais aussi récupérer leurs identifiants. Y a-t-il un moyen de faire ça? 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 étant qu'il existe également une table distincte d'utilisateurs qui se lient à ces organisations, et je voudrais les unifier (par conséquent, supprimez les dupes afin que les utilisateurs se lient à la même organisation au lieu des organisations dupes). Mais je voudrais une partie manuellement pour ne rien gâcher, mais j'aurais toujours besoin d'une déclaration renvoyant les identifiants de toutes les organisations dupes afin de pouvoir parcourir la liste des utilisateurs.

Réponses:


313
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

4
y a-t-il des limitations dans cette requête, par exemple si le nombre d'enregistrements est supérieur à 10 millions?
Steam

3
@Steam Vous avez raison: cette réponse n'est pas efficace dans une base de données plus grande avec des millions d'enregistrements. Préférez la réponse GroupBy / Ayant 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.
Mike Christian

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

2
@KornMuffin Rétrospectivement, mon commentaire sur Count () est nul. L'utilisation d'une évaluation non nulle dans Count () n'est utile que lorsque vous souhaitez compter les résultats non nuls renvoyés par une jointure externe. Sinon, utilisez Count (*). Une grande explication se trouve ici .
Mike Christian

utiliser isnull()pour les colonnes annulables sur la onsection
Arif Ulusoy

92

Vous pouvez exécuter la requête suivante et rechercher 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.


Vous devez l'exécuter exactement des MAX( COUNT(*) ) - 1fois, ce qui pourrait toujours être faisable.
DerMike

1
salut est leur toute façon d'obtenir tous les id au lieu de max id comme pour 2 je peux utiliser max et min mais qu'en est-il de plus de 2? @DerMike
Arijit Mukherjee

31

Vous pouvez le faire comme ceci:

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 souhaitez renvoyer uniquement les enregistrements qui peuvent être supprimés (en laissant un de chacun), 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

Modifier: SQL Server 2000 n'a pas la fonction ROW_NUMBER (). Au lieu de cela, 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

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

SQL Server ne semble pas être capable de reconnaître row_number ()?
xtine

Ah ... avez-vous une ancienne version de SQL Server? Je crois qu'il a été introduit dans SQL Server 2005.
Paul

3
merci encore, chaque fois que je dois le faire
j'arrive

9

La solution marquée comme correcte n'a pas fonctionné pour moi, mais j'ai trouvé cette réponse qui fonctionnait très bien: 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

Vous obtiendrez beaucoup de dupes dans le jeu de résultats, vous devrez donc vous en occuper également.
Renan

1
Si l'identifiant est numérique, la vérification n1.id > n2.idempêchera chaque paire de s'afficher deux fois.
Starwed

9

Vous pouvez l'essayer, c'est 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

n'importe quel moyen pour obtenir tout l'identifiant dans des colonnes séparées par des virgules ou différentes
Arijit Mukherjee

6

Si vous souhaitez supprimer les doublons:

WITH CTE AS(
   SELECT orgName,id,
       RN = ROW_NUMBER()OVER(PARTITION BY orgName ORDER BY Id)
   FROM organizations
)
DELETE FROM CTE WHERE RN > 1

6
select * from [Employees]

Pour trouver un enregistrement en double 1) Utilisation de CTE

with mycte
as
(
select Name,EmailId,ROW_NUMBER() over(partition by Name,EmailId order by id) as Duplicate from [Employees]
)
select * from mycte

2) En utilisant GroupBy

select Name,EmailId,COUNT(name) as Duplicate from  [Employees] group by Name,EmailId 

C'est la solution la plus rapide ici, lorsque vous sélectionnez des données sur des lignes de 10 m. Merci
Fandango68

4
Select * from (Select orgName,id,
ROW_NUMBER() OVER(Partition By OrgName ORDER by id DESC) Rownum
From organizations )tbl Where Rownum>1

Ainsi, les enregistrements avec rowum> 1 seront les enregistrements en double dans votre table. «Partitionner par» le premier groupe par les enregistrements, puis les sérialiser en leur donnant des numéros de série. Donc rownum> 1 sera les enregistrements en double qui pourraient être supprimés en tant que tels.


J'aime celui-ci car il vous permet d'ajouter facilement plus de colonnes dans la clause de sélection interne. Donc, si vous souhaitez renvoyer d'autres colonnes de la table «Organisations», vous n'avez pas à effectuer de «regroupement» sur ces colonnes.
Gwasshoppa


2
select a.orgName,b.duplicate, a.id
from organizations a
inner join (
    SELECT orgName, COUNT(*) AS duplicate
    FROM organizations
    GROUP BY orgName
    HAVING COUNT(*) > 1
) b on o.orgName = oc.orgName
group by a.orgName,a.id

1
select orgname, count(*) as dupes, id 
from organizations
where orgname in (
    select orgname
    from organizations
    group by orgname
    having (count(*) > 1)
)
group by orgname, id

1

Vous avez plusieurs façons de sélectionner duplicate rows.

pour mes solutions, considérons d'abord ce tableau par exemple

CREATE TABLE #Employee
(
ID          INT,
FIRST_NAME  NVARCHAR(100),
LAST_NAME   NVARCHAR(300)
)

INSERT INTO #Employee VALUES ( 1, 'Ardalan', 'Shahgholi' );
INSERT INTO #Employee VALUES ( 2, 'name1', 'lname1' );
INSERT INTO #Employee VALUES ( 3, 'name2', 'lname2' );
INSERT INTO #Employee VALUES ( 2, 'name1', 'lname1' );
INSERT INTO #Employee VALUES ( 3, 'name2', 'lname2' );
INSERT INTO #Employee VALUES ( 4, 'name3', 'lname3' );

Première solution:

SELECT DISTINCT *
FROM   #Employee;

WITH #DeleteEmployee AS (
                     SELECT ROW_NUMBER()
                            OVER(PARTITION BY ID, First_Name, Last_Name ORDER BY ID) AS
                            RNUM
                     FROM   #Employee
                 )

SELECT *
FROM   #DeleteEmployee
WHERE  RNUM > 1

SELECT DISTINCT *
FROM   #Employee

Seconde solution: utiliser le identitychamp

SELECT DISTINCT *
FROM   #Employee;

ALTER TABLE #Employee ADD UNIQ_ID INT IDENTITY(1, 1)

SELECT *
FROM   #Employee
WHERE  UNIQ_ID < (
    SELECT MAX(UNIQ_ID)
    FROM   #Employee a2
    WHERE  #Employee.ID = a2.ID
           AND #Employee.FIRST_NAME = a2.FIRST_NAME
           AND #Employee.LAST_NAME = a2.LAST_NAME
)

ALTER TABLE #Employee DROP COLUMN UNIQ_ID

SELECT DISTINCT *
FROM   #Employee

et à la fin de toute solution, utilisez cette commande

DROP TABLE #Employee

0

Je pense que je sais ce dont vous avez besoin, j'avais besoin de mélanger les réponses et je pense avoir la solution qu'il voulait:

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

avoir l'id max vous donnera l'id du dublic et celui de l'original qui est ce qu'il a demandé:

id org name , dublicate count (missing out in this case) 
id doublicate org name , doub count (missing out again because does not help in this case)

seule triste chose que vous obtenez sous cette forme

id , name , dubid , name

j'espère que ça aide encore


0

Supposons que nous ayons un tableau "Student" avec 2 colonnes:

  • student_id int
  • student_name varchar

    Records:
    +------------+---------------------+
    | student_id | student_name        |
    +------------+---------------------+
    |        101 | usman               |
    |        101 | usman               |
    |        101 | usman               |
    |        102 | usmanyaqoob         |
    |        103 | muhammadusmanyaqoob |
    |        103 | muhammadusmanyaqoob |
    +------------+---------------------+

Maintenant, nous voulons voir les enregistrements en double Utilisez cette requête:

select student_name,student_id ,count(*) c from student group by student_id,student_name having c>1;

+---------------------+------------+---+
| student_name        | student_id | c |
+---------------------+------------+---+
| usman               |        101 | 3 |
| muhammadusmanyaqoob |        103 | 2 |
+---------------------+------------+---+

0

J'ai une meilleure option pour obtenir les enregistrements en double dans une table

SELECT x.studid, y.stdname, y.dupecount
FROM student AS x INNER JOIN
(SELECT a.stdname, COUNT(*) AS dupecount
FROM student AS a INNER JOIN
studmisc AS b ON a.studid = b.studid
WHERE (a.studid LIKE '2018%') AND (b.studstatus = 4)
GROUP BY a.stdname
HAVING (COUNT(*) > 1)) AS y ON x.stdname = y.stdname INNER JOIN
studmisc AS z ON x.studid = z.studid
WHERE (x.studid LIKE '2018%') AND (z.studstatus = 4)
ORDER BY x.stdname

Le résultat de la requête ci-dessus montre tous les noms en double avec des identifiants d'étudiant uniques et le nombre de répétitions

Cliquez ici pour voir le résultat du sql


0
 /*To get duplicate data in table */

 SELECT COUNT(EmpCode),EmpCode FROM tbl_Employees WHERE Status=1 
  GROUP BY EmpCode HAVING COUNT(EmpCode) > 1

0

J'utilise deux méthodes pour trouver des lignes en double. La 1ère méthode est la plus célèbre utilisant le groupe par et en ayant. La deuxième méthode utilise CTE - Common Table Expression .

Comme mentionné par @RedFilter, cette manière est également correcte. Plusieurs fois, je trouve que la méthode CTE est également utile pour moi.

WITH TempOrg (orgName,RepeatCount)
AS
(
SELECT orgName,ROW_NUMBER() OVER(PARTITION by orgName ORDER BY orgName) 
AS RepeatCount
FROM dbo.organizations
)
select t.*,e.id from organizations   e
inner join TempOrg t on t.orgName= e.orgName
where t.RepeatCount>1

Dans l'exemple ci-dessus, nous avons collecté le résultat en trouvant l'occurrence de répétition à l'aide de ROW_NUMBER et PARTITION BY. Ensuite, nous avons appliqué la clause where pour sélectionner uniquement les lignes dont le nombre de répétitions est supérieur à 1. Tout le résultat est collecté dans la table CTE et joint à la table Organisations.

Source: CodoBee


-2

Essayer

SELECT orgName, id, count(*) as dupes
FROM organizations
GROUP BY orgName, id
HAVING count(*) > 1;
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.