web-dev-qa-db-fra.com

Supprimer les enregistrements en double dans PostgreSQL

J'ai une table dans une base de données PostgreSQL 8.3.8, qui n'a pas de clé/contrainte sur elle, et a plusieurs lignes avec exactement les mêmes valeurs.

Je souhaite supprimer tous les doublons et ne conserver qu'une copie de chaque ligne.

Une colonne en particulier (nommée "clé") peut être utilisée pour identifier les doublons (c’est-à-dire qu’il ne doit exister qu’une entrée pour chaque "clé" distincte).

Comment puis-je faire ceci? (idéalement avec une seule commande SQL) La vitesse n’est pas un problème dans ce cas (il n’ya que quelques lignes).

74
André Morujão
DELETE FROM dupes a
WHERE a.ctid <> (SELECT min(b.ctid)
                 FROM   dupes b
                 WHERE  a.key = b.key);
56

Une solution plus rapide est

DELETE FROM dups a USING (
      SELECT MIN(ctid) as ctid, key
        FROM dups 
        GROUP BY key HAVING COUNT(*) > 1
      ) b
      WHERE a.key = b.key 
      AND a.ctid <> b.ctid
115
rapimo

C'est rapide et concis:

DELETE FROM dupes T1
    USING   dupes T2
WHERE   T1.ctid < T2.ctid  -- delete the older versions
    AND T1.key  = T2.key;  -- add more columns if needed

Voir aussi ma réponse à Comment supprimer des lignes en double sans identifiant unique qui inclut plus d'informations.

28
isapir

J'ai essayé ceci:

DELETE FROM tablename
WHERE id IN (SELECT id
              FROM (SELECT id,
                             ROW_NUMBER() OVER (partition BY column1, column2, column3 ORDER BY id) AS rnum
                     FROM tablename) t
              WHERE t.rnum > 1);

fourni par Postgres wiki:

https://wiki.postgresql.org/wiki/Deleting_duplicates

13
Radu Gabriel

Je devais créer ma propre version. La version écrite par @a_horse_with_no_name est beaucoup trop lente sur ma table (21 millions de lignes). Et @rapimo ne supprime tout simplement pas les doublons.

Voici ce que j'utilise sur PostgreSQL 9.5

DELETE FROM your_table
WHERE ctid IN (
  SELECT unnest(array_remove(all_ctids, actid))
  FROM (
         SELECT
           min(b.ctid)     AS actid,
           array_agg(ctid) AS all_ctids
         FROM your_table b
         GROUP BY key1, key2, key3, key4
         HAVING count(*) > 1) c);
6
expert

Je voudrais utiliser une table temporaire:

create table tab_temp as
select distinct f1, f2, f3, fn
  from tab;

Ensuite, supprimez tab et renommez tab_temp en tab.

4
Pablo Santa Cruz

Cela a bien fonctionné pour moi. J'ai eu une table, termes, qui contenait des valeurs en double. Exécution d'une requête pour remplir une table temporaire avec toutes les lignes en double. Ensuite, j'ai exécuté l'instruction a delete avec ces identifiants dans la table temporaire. valeur est la colonne qui contenait les doublons. 

        CREATE TEMP TABLE dupids AS
        select id from (
                    select value, id, row_number() 
over (partition by value order by value) 
    as rownum from terms
                  ) tmp
                  where rownum >= 2;

delete from [table] where id in (select id from dupids)
0
Beanwah