Certains serveurs SQL ont une fonctionnalité où INSERT
est ignoré s'il enfreint une contrainte de clé primaire/unique. Par exemple, MySQL a INSERT IGNORE
.
Quelle est la meilleure façon d'émuler INSERT IGNORE
et ON DUPLICATE KEY UPDATE
avec PostgreSQL?
Essayez de faire une mise à jour. S'il ne modifie aucune ligne, cela signifie qu'elle n'existe pas, faites une insertion. Évidemment, vous faites cela dans une transaction.
Vous pouvez bien sûr envelopper cette fonction dans une fonction si vous ne voulez pas mettre le code supplémentaire du côté client. Vous avez également besoin d'une boucle pour la condition de course très rare dans cette pensée.
Il existe un exemple dans la documentation: http://www.postgresql.org/docs/9.3/static/plpgsql-control-structures.html , exemple 40-2 tout en bas.
C'est généralement le moyen le plus simple. Vous pouvez faire de la magie avec des règles, mais cela risque d'être beaucoup plus compliqué. Je recommanderais l'approche intégrale sur cette journée.
Cela fonctionne pour les valeurs à une seule ligne ou à quelques lignes. Si vous traitez de grandes quantités de lignes, par exemple à partir d'une sous-requête, vous feriez mieux de la scinder en deux requêtes, une pour INSERT et l'autre pour UPDATE (en tant que jointure/sous-sélection appropriée, bien sûr - inutile d'écrire votre commande principale). filtrer deux fois)
Avec PostgreSQL 9.5, ceci est maintenant fonctionnalité native (comme MySQL a depuis plusieurs années):
INSERT ... SUR UN CONFLIT, NE RIEN/METTRE À JOUR ("UPSERT")
9.5 apporte un soutien pour les opérations "UPSERT". INSERT est étendu pour accepter une clause ON CONFLICT DO UPDATE/IGNORE. Cette clause spécifie une action alternative à prendre en cas de violation supposée dupliquée.
...
Autre exemple de nouvelle syntaxe:
INSERT INTO user_logins (username, logins)
VALUES ('Naomi',1),('James',1)
ON CONFLICT (username)
DO UPDATE SET logins = user_logins.logins + EXCLUDED.logins;
Edit: au cas où vous auriez manqué réponse de Warren , PG9.5 l’a maintenant en mode natif; le temps de mettre à niveau!
En s'appuyant sur la réponse de Bill Karwin, précisez à quoi ressemblerait une approche basée sur des règles (transfert à partir d'un autre schéma de la même base de données et avec une clé primaire à plusieurs colonnes):
CREATE RULE "my_table_on_duplicate_ignore" AS ON INSERT TO "my_table"
WHERE EXISTS(SELECT 1 FROM my_table
WHERE (pk_col_1, pk_col_2)=(NEW.pk_col_1, NEW.pk_col_2))
DO INSTEAD NOTHING;
INSERT INTO my_table SELECT * FROM another_schema.my_table WHERE some_cond;
DROP RULE "my_table_on_duplicate_ignore" ON "my_table";
Remarque: La règle s'applique à toutes les opérations INSERT
jusqu'à ce que la règle soit supprimée, donc pas tout à fait ad hoc.
Pour obtenir la logique insert ignore, vous pouvez faire quelque chose comme ci-dessous. J'ai trouvé que l'insertion simplement à partir d'une instruction select de valeurs littérales fonctionnait mieux. Vous pouvez ensuite masquer les clés en double avec une clause NOT EXISTS. Pour obtenir la mise à jour sur la logique en double, je soupçonne qu'une boucle pl/pgsql serait nécessaire.
INSERT INTO manager.vin_manufacturer
(SELECT * FROM( VALUES
('935',' Citroën Brazil','Citroën'),
('ABC', 'Toyota', 'Toyota'),
('ZOM',' OM','OM')
) as tmp (vin_manufacturer_id, manufacturer_desc, make_desc)
WHERE NOT EXISTS (
--ignore anything that has already been inserted
SELECT 1 FROM manager.vin_manufacturer m where m.vin_manufacturer_id = tmp.vin_manufacturer_id)
)
Pour ceux d'entre vous qui ont Postgres 9.5 ou supérieur, la nouvelle syntaxe ON CONFLICT DO RING devrait fonctionner:
INSERT INTO target_table (field_one, field_two, field_three )
SELECT field_one, field_two, field_three
FROM source_table
ON CONFLICT (field_one) DO NOTHING;
Pour ceux d'entre nous qui ont une version antérieure, cette jointure de droits fonctionnera à la place:
INSERT INTO target_table (field_one, field_two, field_three )
SELECT source_table.field_one, source_table.field_two, source_table.field_three
FROM source_table
LEFT JOIN target_table ON source_table.field_one = target_table.field_one
WHERE target_table.field_one IS NULL;
INSERT INTO mytable(col1,col2)
SELECT 'val1','val2'
WHERE NOT EXISTS (SELECT 1 FROM mytable WHERE col1='val1')
On dirait que PostgreSQL supporte un objet de schéma appelé règle .
http://www.postgresql.org/docs/current/static/rules-update.html
Vous pouvez créer une règle ON INSERT
pour une table donnée, le faisant NOTHING
si une ligne existe avec la valeur de clé primaire donnée, ou bien le faire faire un UPDATE
au lieu du INSERT
si la ligne existe avec la valeur de clé primaire donnée.
Je n'ai pas essayé cela moi-même, je ne peux donc ni parler d'expérience ni donner un exemple.
Comme @hanmari l'a mentionné dans son commentaire. lors de l'insertion dans une table postgres, le on conflict (..) ne rien faire est le meilleur code à utiliser pour ne pas insérer de données dupliquées .:
query = "INSERT INTO db_table_name(column_name)
VALUES(%s) ON CONFLICT (column_name) DO NOTHING;"
La ligne de code ON CONFLICT permettra à l'instruction insert d'insérer des lignes de données. Le code de requête et de valeurs est un exemple de date insérée d'un fichier Excel dans une table postgres db. J'ai des contraintes ajoutées à une table postgres que j'utilise pour m'assurer que le champ ID est unique. Au lieu d'exécuter une suppression sur des lignes de données identiques, j'ajoute une ligne de code SQL qui renumérote la colonne ID commençant à 1. Exemple:
q = 'ALTER id_column serial RESTART WITH 1'
Si mes données ont un champ ID, je ne l'utilise pas comme ID principal/ID de série, je crée une colonne ID et je le configure en série. J'espère que cette information est utile à tout le monde. * Je n'ai pas de diplôme universitaire en développement/codage de logiciels. Tout ce que je sais en codage, j'étudie seul.
Cette solution évite d'utiliser des règles:
BEGIN
INSERT INTO tableA (unique_column,c2,c3) VALUES (1,2,3);
EXCEPTION
WHEN unique_violation THEN
UPDATE tableA SET c2 = 2, c3 = 3 WHERE unique_column = 1;
END;
mais cela présente un inconvénient en termes de performances (voir PostgreSQL.org ):
Un bloc contenant une clause EXCEPTION est nettement plus coûteux à entrer et à sortir qu'un bloc sans un. Par conséquent, n'utilisez pas EXCEPTION sans nécessité.
En vrac, vous pouvez toujours supprimer la ligne avant l'insertion. La suppression d’une ligne qui n’existe pas ne provoque pas d’erreur, elle est donc ignorée en toute sécurité.