Suppression des lignes en double de la table dans Oracle


151

Je teste quelque chose dans Oracle et remplit une table avec des exemples de données, mais dans le processus, j'ai accidentellement chargé des enregistrements en double, donc maintenant je ne peux pas créer de clé primaire en utilisant certaines des colonnes.

Comment puis-je supprimer toutes les lignes en double et n'en laisser qu'une seule?

Réponses:


306

Utilisez la rowidpseudo - colonne.

DELETE FROM your_table
WHERE rowid not in
(SELECT MIN(rowid)
FROM your_table
GROUP BY column1, column2, column3);

column1, column2et column3composez la clé d'identification pour chaque enregistrement. Vous pouvez lister toutes vos colonnes.


6
+1 J'ai dû trouver deux numéros de téléphone en double enfouis dans plus de 12 000 enregistrements. Changé le DELETE en SELECT et cela les a trouvés en quelques secondes. M'a fait gagner beaucoup de temps, merci.
shimonk le

3
Cette approche n'a pas fonctionné pour moi. Je ne sais pas pourquoi. Quand j'ai remplacé "DELETE" par "SELECT *", il a renvoyé les lignes que je voulais supprimer, mais quand j'ai exécuté avec "DELETE", il était juste suspendu indéfiniment.
aro_biz

Le mien est soit suspendu soit simplement extrêmement long. Courant depuis environ 22 heures et toujours en cours. La table contient 21 millions d'enregistrements.
Cameron Castillo

Je suggère d'ajouter un filtrage supplémentaire à l'instruction WHERE si vous avez un très grand ensemble de données et si possible, cela pourrait aider les gens avec des requêtes de longue durée.
Ricardo Sanchez

2
Si la sélection fonctionne, mais pas la suppression, cela peut être dû à la taille de la sous-requête résultante. Il peut être intéressant de commencer par créer une table avec le résultat de la sous-requête, de créer un index sur la colonne min (rowid), puis d'exécuter l'instruction de suppression.
Wouter

14

De Ask Tom

delete from t
 where rowid IN ( select rid
                    from (select rowid rid, 
                                 row_number() over (partition by 
                         companyid, agentid, class , status, terminationdate
                                   order by rowid) rn
                            from t)
                   where rn <> 1);

(correction de la parenthèse manquante)


1
Parenthèse manquante dans l'instruction. Je suppose que ça devrait être à la fin?
Cameron Castillo

12

Depuis DevX.com :

DELETE FROM our_table
WHERE rowid not in
(SELECT MIN(rowid)
FROM our_table
GROUP BY column1, column2, column3...) ;

Où colonne1, colonne2, etc. est la clé que vous souhaitez utiliser.


12
DELETE FROM tablename a
      WHERE a.ROWID > ANY (SELECT b.ROWID
                             FROM tablename b
                            WHERE a.fieldname = b.fieldname
                              AND a.fieldname2 = b.fieldname2)

1
Concernant mon commentaire ci-dessus sur la réponse la plus votée, c'est cette demande qui a en fait résolu mon problème.
aro_biz

2
Ce sera-beaucoup plus lent sur d'énormes tables que la solution de Bill.
Wouter

8

Solution 1)

delete from emp
where rowid not in
(select max(rowid) from emp group by empno);

Solution 2)

delete from emp where rowid in
               (
                 select rid from
                  (
                    select rowid rid,
                      row_number() over(partition by empno order by empno) rn
                      from emp
                  )
                where rn > 1
               );

Solution 3)

delete from emp e1
         where rowid not in
          (select max(rowid) from emp e2
           where e1.empno = e2.empno ); 

6

créer la table t2 comme select distinct * de t1;


pas une réponse - distinct *prendra chaque enregistrement qui diffère d'au moins 1 symbole dans 1 colonne. Tout ce dont vous avez besoin est de sélectionner des valeurs distinctes uniquement à partir des colonnes pour lesquelles vous souhaitez créer des clés primaires - la réponse de Bill est un excellent exemple de cette approche.
Nogard

1
C'était ce dont j'avais besoin (supprimer les lignes entièrement identiques). Merci !
Emmanuel

Un autre inconvénient de cette méthode est que vous devez créer une copie de votre table. Pour les tables volumineuses, cela implique de fournir un tablespace supplémentaire et de supprimer ou de réduire le tablespace après la copie. La méthode de Bill a plus d'avantages et aucun inconvénient supplémentaire.
Wouter

3

Vous devriez faire un petit bloc pl / sql en utilisant un curseur pour la boucle et supprimer les lignes que vous ne voulez pas conserver. Par exemple:

declare
prev_var my_table.var1%TYPE;

begin

for t in (select var1 from my_table order by var 1) LOOP

-- if previous var equal current var, delete the row, else keep on going.
end loop;

end;

Je crois que le vote négatif est dû au fait que vous utilisez PL / SQL lorsque vous pouvez le faire en SQL, au cas où vous vous poseriez la question.
WW.

7
Ce n'est pas parce que vous pouvez le faire en SQL que c'est la seule solution. J'ai posté cette solution, après avoir vu la solution SQL uniquement. Je pensais que les votes négatifs étaient des réponses incorrectes.
Nick

3

Pour sélectionner les doublons, seul le format de requête peut être:

SELECT GroupFunction(column1), GroupFunction(column2),..., 
COUNT(column1), column1, column2...
FROM our_table
GROUP BY column1, column2, column3...
HAVING COUNT(column1) > 1

Ainsi, la requête correcte selon une autre suggestion est:

DELETE FROM tablename a
      WHERE a.ROWID > ANY (SELECT b.ROWID
                             FROM tablename b
                            WHERE a.fieldname = b.fieldname
                              AND a.fieldname2 = b.fieldname2
                              AND ....so on.. to identify the duplicate rows....)

Cette requête conservera l'enregistrement le plus ancien de la base de données pour les critères choisis dans le WHERE CLAUSE.

Associé certifié Oracle (2008)


2

Le moyen le plus rapide pour les très grandes tables

  1. Créez une table d'exceptions avec la structure ci-dessous: exceptions_table

    ROW_ID ROWID
    OWNER VARCHAR2(30)
    TABLE_NAME VARCHAR2(30)
    CONSTRAINT VARCHAR2(30)
  2. Essayez de créer une contrainte unique ou une clé primaire qui sera violée par les doublons. Vous recevrez un message d'erreur car vous avez des doublons. La table des exceptions contiendra les rowids des lignes dupliquées.

    alter table add constraint
    unique --or primary key
    (dupfield1,dupfield2) exceptions into exceptions_table;
  3. Rejoignez votre table avec exceptions_table par rowid et supprimez les dups

    delete original_dups where rowid in (select ROW_ID from exceptions_table);
  4. Si le nombre de lignes à supprimer est important, créez une nouvelle table (avec toutes les subventions et tous les index) anti-jointure avec exceptions_table par rowid et renommez la table d'origine en table original_dups et renommez new_table_with_no_dups en table d'origine

    create table new_table_with_no_dups AS (
        select field1, field2 ........ 
        from original_dups t1
        where not exists ( select null from exceptions_table T2 where t1.rowid = t2.row_id )
    )

2

Utilisation de rowid-

delete from emp
 where rowid not in
 (select max(rowid) from emp group by empno);

Utilisation de l'auto-jointure

delete from emp e1
 where rowid not in
 (select max(rowid) from emp e2
 where e1.empno = e2.empno );

Salut Tandale, Veuillez utiliser l'outil de formatage de code lors de la soumission des réponses, car il améliore la lisibilité.
NSNoob

2

Solution 4)

 delete from emp where rowid in
            (
             select rid from
                (
                  select rowid rid,
                  dense_rank() over(partition by empno order by rowid
                ) rn
             from emp
            )
 where rn > 1
);

Pouvez-vous expliquer un peu?
Dieter Meemken

Le rang dense avec partition par donne le rang des lignes en double avec le même nombre, par exemple trois lignes ayant le rang 1, 1, 1 et rowid créent pour chaque ligne comme unic et nous essayons de supprimer les rowids qui ne correspondent pas.
DoOrDie

nous pouvons utiliser les fonctions rank et dense_rank mais je pense que rank fonctionne parfaitement dans ce scénario.
DoOrDie

2

1. solution

delete from emp
    where rowid not in
    (select max(rowid) from emp group by empno);

2. sloution

delete from emp where rowid in
               (
                 select rid from
                  (
                    select rowid rid,
                      row_number() over(partition by empno order by empno) rn
                      from emp
                  )
                where rn > 1
               );

3. solution

delete from emp e1
         where rowid not in
          (select max(rowid) from emp e2
           where e1.empno = e2.empno ); 

4. solution

 delete from emp where rowid in
            (
             select rid from
                (
                  select rowid rid,
                  dense_rank() over(partition by empno order by rowid
                ) rn
             from emp
            )
 where rn > 1
);

2

5. solution

delete from emp where rowid in 
    (
      select  rid from
       (
         select rowid rid,rank() over (partition by emp_id order by rowid)rn from emp     
       )
     where rn > 1
    );

2
DELETE from table_name where rowid not in (select min(rowid) FROM table_name group by column_name);

et vous pouvez également supprimer les enregistrements en double d'une autre manière

DELETE from table_name a where rowid > (select min(rowid) FROM table_name b where a.column=b.column);

2
create table abcd(id number(10),name varchar2(20))

insert into abcd values(1,'abc')

insert into abcd values(2,'pqr')


insert into abcd values(3,'xyz')

insert into abcd values(1,'abc')

insert into abcd values(2,'pqr')

insert into abcd values(3,'xyz')


select * from abcd
id  Name
1   abc
2   pqr
3   xyz
1   abc
2   pqr
3   xyz

Delete Duplicate record but keep Distinct Record in table 

DELETE 
FROM abcd a
WHERE ROWID > (SELECT MIN(ROWID) FROM abcd b
WHERE b.id=a.id
);

run the above query 3 rows delete 

select * from abcd

id  Name 
1   abc
2   pqr
3   xyz

1
DELETE FROM tableName  WHERE ROWID NOT IN (SELECT   MIN (ROWID) FROM table GROUP BY columnname);

Même réponse que la réponse plus élaborée de Bill the Lizard.
Wouter

1
delete from dept
where rowid in (
     select rowid
     from dept
     minus
     select max(rowid)
     from dept
     group by DEPTNO, DNAME, LOC
);

Pouvez-vous ajouter plus d'informations sur votre chemin? Merci.
Reporter

1

Pour de meilleures performances, voici ce que j'ai écrit:
(voir plan d'exécution)

DELETE FROM your_table
WHERE rowid IN 
  (select t1.rowid from your_table  t1
      LEFT OUTER JOIN (
      SELECT MIN(rowid) as rowid, column1,column2, column3
      FROM your_table 
      GROUP BY column1, column2, column3
  )  co1 ON (t1.rowid = co1.rowid)
  WHERE co1.rowid IS NULL
);

1

Vérifiez ci-dessous les scripts -

1.

Create table test(id int,sal int); 

2.

    insert into test values(1,100);    
    insert into test values(1,100);    
    insert into test values(2,200);    
    insert into test values(2,200);    
    insert into test values(3,300);    
    insert into test values(3,300);    
    commit;

3.

 select * from test;    

Vous verrez ici 6 disques.
4. exécuter la requête ci-dessous -

delete from 
   test
where rowid in
 (select rowid from 
   (select 
     rowid,
     row_number()
    over 
     (partition by id order by sal) dup
    from test)
  where dup > 1)
  1. select * from test;

Vous verrez que les enregistrements en double ont été supprimés.
J'espère que cela résout votre requête. Merci :)


1

Je n'ai vu aucune réponse utilisant des expressions de table et des fonctions de fenêtre courantes. C'est ce avec quoi je trouve le plus facile de travailler.

DELETE FROM
 YourTable
WHERE
 ROWID IN
    (WITH Duplicates
          AS (SELECT
               ROWID RID, 
               ROW_NUMBER() 
               OVER(
               PARTITION BY First_Name, Last_Name, Birth_Date)
                  AS RN
               SUM(1)
               OVER(
               PARTITION BY First_Name, Last_Name, Birth_Date
               ORDER BY ROWID ROWS BETWEEN UNBOUNDED PRECEDING 
                                       AND UNBOUNDED FOLLOWING)
                   AS CNT
              FROM
               YourTable
              WHERE
               Load_Date IS NULL)
     SELECT
      RID
     FROM
      duplicates
     WHERE
      RN > 1);

Quelque chose à noter:

1) Nous vérifions uniquement la duplication sur les champs de la clause de partition.

2) Si vous avez une raison de choisir un doublon par rapport aux autres, vous pouvez utiliser une clause order by pour que cette ligne ait row_number () = 1

3) Vous pouvez modifier le numéro en double conservé en modifiant la clause where finale en "Where RN> N" avec N> = 1 (je pensais que N = 0 supprimerait toutes les lignes qui ont des doublons, mais cela supprimerait simplement toutes les lignes) .

4) Ajout du champ de partition Sum à la requête CTE qui marquera chaque ligne avec le nombre de lignes dans le groupe. Donc, pour sélectionner des lignes avec des doublons, y compris le premier élément, utilisez "WHERE cnt> 1".


0
create or replace procedure delete_duplicate_enq as
    cursor c1 is
    select *
    from enquiry;
begin
    for z in c1 loop
        delete enquiry
        where enquiry.enquiryno = z.enquiryno
        and rowid > any
        (select rowid
        from enquiry
        where enquiry.enquiryno = z.enquiryno);
    end loop;
 end delete_duplicate_enq;

Un inconvénient majeur de cette méthode est la jointure interne. Pour les grandes tables, ce sera beaucoup plus lent que la méthode de Bill. De plus, utiliser PL / SQL pour ce faire est excessif, vous pouvez également l'utiliser en utilisant simplement sql.
Wouter

0

Solution :

delete from emp where rowid in
(
    select rid from
    (
        select rowid rid,
        row_number() over(partition by empno order by empno) rn
        from emp
    )
    where rn > 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.