Je dois ajouter une contrainte unique à une table existante. C'est bien sauf que la table contient déjà des millions de lignes et que de nombreuses lignes violent la contrainte unique que je dois ajouter.
Quelle est l'approche la plus rapide pour supprimer les lignes incriminées? J'ai une instruction SQL qui trouve les doublons et les supprime, mais cela prend une éternité à s'exécuter. Existe-t-il une autre façon de résoudre ce problème? Peut-être sauvegarder la table, puis restaurer après l'ajout de la contrainte?
CREATE TABLE tmp AS SELECT ...;
. Ensuite, vous n'avez même pas besoin de comprendre quelle est la dispositiontmp
. :)Certaines de ces approches semblent un peu compliquées, et je le fais généralement comme:
Compte tenu de la table
table
, souhaitez l'unifier sur (champ1, champ2) en gardant la ligne avec le champ max3:Par exemple, j'ai une table,
user_accounts
et je veux ajouter une contrainte unique sur le courrier électronique, mais j'ai quelques doublons. Dites aussi que je veux garder le plus récemment créé (id max parmi les doublons).USING
n'est pas du SQL standard, c'est une extension PostgreSQL (mais très utile), mais la question originale mentionne spécifiquement PostgreSQL.la source
USING
postgresql?WHERE table1.ctid<table2.ctid
- pas besoin d'ajouter une colonne sérielleAu lieu de créer une nouvelle table, vous pouvez également réinsérer des lignes uniques dans la même table après l'avoir tronquée. Faites tout cela en une seule transaction . Si vous le souhaitez, vous pouvez supprimer automatiquement la table temporaire à la fin de la transaction avec
ON COMMIT DROP
. Voir ci-dessous.Cette approche n'est utile que s'il y a beaucoup de lignes à supprimer de partout dans la table. Pour quelques doublons, utilisez un simple
DELETE
.Vous avez mentionné des millions de lignes. Pour accélérer l'opération, vous souhaitez allouer suffisamment de tampons temporaires pour la session. Le paramètre doit être ajusté avant qu'un tampon temporaire ne soit utilisé dans votre session en cours. Découvrez la taille de votre table:
Réglez en
temp_buffers
conséquence. Arrondissez généreusement car la représentation en mémoire nécessite un peu plus de RAM.Cette méthode peut être supérieure à la création d'une nouvelle table si des objets dépendants existent. Vues, index, clés étrangères ou autres objets référençant la table.
TRUNCATE
vous permet de commencer avec une ardoise vierge de toute façon (nouveau fichier en arrière-plan) et est beaucoup plus rapide qu'avecDELETE FROM tbl
de grandes tables (DELETE
peut en fait être plus rapide avec de petites tables).Pour les grandes tables, il est régulièrement plus rapide de supprimer les index et les clés étrangères, de remplir la table et de recréer ces objets. En ce qui concerne les contraintes fk, vous devez être certain que les nouvelles données sont bien sûr valides, sinon vous rencontrerez une exception en essayant de créer le fk.
Notez que cela
TRUNCATE
nécessite un verrouillage plus agressif queDELETE
. Cela peut être un problème pour les tables à forte charge simultanée.Si ce
TRUNCATE
n'est pas une option ou généralement pour les tables petites à moyennes, il existe une technique similaire avec un CTE de modification des données (Postgres 9.1 +):Plus lent pour les grandes tables, car
TRUNCATE
c'est plus rapide là-bas. Mais peut-être plus rapide (et plus simple!) Pour les petites tables.Si vous n'avez aucun objet dépendant du tout, vous pouvez créer une nouvelle table et supprimer l'ancienne, mais vous ne gagnerez pratiquement rien à cette approche universelle.
Pour les très grandes tables qui ne rentreraient pas dans la RAM disponible , la création d'une nouvelle table sera considérablement plus rapide. Vous devrez mettre cela en balance avec d'éventuels problèmes / frais généraux avec des objets dépendants.
la source
TRUNCATE
. Comme l'a dit Erwin, assurez-vous qu'il existe avant de tronquer votre table. Voir la réponse de @ codebykatON COMMIT DROP
, pour que les personnes qui manquent la partie où j'ai écrit "en une seule transaction" ne perdent pas de données. Et j'ai ajouté BEGIN / COMMIT pour clarifier "une transaction".Vous pouvez utiliser oid ou ctid, qui sont normalement des colonnes "non visibles" dans le tableau:
la source
NOT EXISTS
devrait être considérablement plus rapide :DELETE FROM tbl t WHERE EXISTS (SELECT 1 FROM tbl t1 WHERE t1.dist_col = t.dist_col AND t1.ctid > t.ctid)
- ou utilisez toute autre colonne ou ensemble de colonnes pour le tri pour choisir un survivant.NOT EXISTS
?EXISTS
ici. Lisez-le comme ceci: "Supprimez toutes les lignes où une autre ligne existe avec la même valeurdist_col
mais une plus grandectid
". Le seul survivant par groupe de dupes sera celui qui aura le plus grosctid
.LIMIT
si vous connaissez le nombre de doublons.La fonction de fenêtre PostgreSQL est pratique pour ce problème.
Voir Suppression des doublons .
la source
À partir d' une ancienne liste de diffusion postgresql.org :
Des valeurs uniques
Dupliquer les valeurs
Encore un double double
Sélectionnez les lignes en double
Supprimer les lignes en double
Remarque: PostgreSQL ne prend pas en charge les alias sur la table mentionnée dans la
from
clause de suppression.la source
Requête généralisée pour supprimer les doublons:
La colonne
ctid
est une colonne spéciale disponible pour chaque table mais non visible sauf mention contraire. Lactid
valeur de la colonne est considérée comme unique pour chaque ligne d'une table.la source
GROUP BY
clause - cela devrait être le `` critère d'unicité '' qui est violé maintenant ou si vous souhaitez que la clé détecte les doublons. Si spécifié incorrect, cela ne fonctionnera pas correctementJe viens d'utiliser la réponse d'Erwin Brandstetter avec succès pour supprimer les doublons dans une table de jointure (une table ne disposant pas de ses propres ID primaires), mais j'ai trouvé qu'il y avait une mise en garde importante.
Y compris
ON COMMIT DROP
signifie que la table temporaire sera supprimée à la fin de la transaction. Pour moi, cela signifiait que la table temporaire n'était plus disponible au moment où je suis allé l'insérer!Je viens de le faire
CREATE TEMPORARY TABLE t_tmp AS SELECT DISTINCT * FROM tbl;
et tout a bien fonctionné.La table temporaire est supprimée à la fin de la session.
la source
Cette fonction supprime les doublons sans supprimer les index et le fait à n'importe quelle table.
Usage:
select remove_duplicates('mytable');
la source
la source
Si vous n'avez qu'une ou quelques entrées dupliquées, et qu'elles sont effectivement dupliquées (c'est-à-dire qu'elles apparaissent deux fois), vous pouvez utiliser la
ctid
colonne "masquée" , comme proposé ci-dessus, avecLIMIT
:Cela supprimera uniquement la première des lignes sélectionnées.
la source
Tout d'abord, vous devez décider lesquels de vos "doublons" vous conserverez. Si toutes les colonnes sont égales, OK, vous pouvez supprimer l'une d'entre elles ... Mais peut-être voulez-vous ne conserver que le plus récent, ou un autre critère?
Le moyen le plus rapide dépend de votre réponse à la question ci-dessus, ainsi que du% de doublons sur la table. Si vous jetez 50% de vos lignes, vous feriez mieux de le faire
CREATE TABLE ... AS SELECT DISTINCT ... FROM ... ;
, et si vous supprimez 1% des lignes, il est préférable d'utiliser DELETE.Aussi pour des opérations de maintenance comme celle-ci, il est généralement bon de définir
work_mem
une bonne partie de votre RAM: exécutez EXPLAIN, vérifiez le nombre N de tris / hachages et définissez work_mem sur votre RAM / 2 / N. Utilisez beaucoup de RAM; c'est bon pour la vitesse. Tant que vous n'avez qu'une seule connexion simultanée ...la source
Je travaille avec PostgreSQL 8.4. Lorsque j'ai exécuté le code proposé, j'ai constaté qu'il ne supprimait pas réellement les doublons. En exécutant certains tests, j'ai trouvé que l'ajout de "DISTINCT ON (duplicate_column_name)" et de "ORDER BY duplicate_column_name" a fait l'affaire. Je ne suis pas un gourou SQL, j'ai trouvé cela dans la doc de PostgreSQL 8.4 SELECT ... DISTINCT.
la source
Cela fonctionne très bien et est très rapide:
la source
Supprimez les doublons par colonne (s) et conservez la ligne avec l'ID le plus bas. Le motif est tiré du wiki postgres
En utilisant les CTE, vous pouvez obtenir une version plus lisible de ce qui précède grâce à ce
la source
la source