J'ai le tableau de compteurs suivant:
CREATE TABLE cache (
key text PRIMARY KEY,
generation int
);
Je voudrais incrémenter l'un des compteurs ou le mettre à zéro si la ligne correspondante n'existe pas encore. Existe-t-il un moyen de le faire sans problèmes de concurrence dans SQL standard? L'opération fait parfois partie d'une transaction, parfois séparée.
Le SQL doit s'exécuter sans modification sur SQLite, PostgreSQL et MySQL, si possible.
Une recherche a donné plusieurs idées qui souffrent de problèmes de concurrence ou sont spécifiques à une base de données:
Essayez
INSERT
une nouvelle ligne etUPDATE
s'il y a eu une erreur. Malheureusement, l'erreur surINSERT
abandonne la transaction en cours.UPDATE
la ligne, et si aucune ligne n'a été modifiée,INSERT
une nouvelle ligne.MySQL a une
ON DUPLICATE KEY UPDATE
clause.
EDIT: Merci pour toutes les bonnes réponses. Il semble que Paul a raison, et il n'y a pas un seul moyen portable de le faire. C'est assez surprenant pour moi, car cela ressemble à une opération très basique.
Réponses:
MySQL (et ensuite SQLite) prennent également en charge la syntaxe REPLACE INTO:
REPLACE INTO my_table (pk_id, col1) VALUES (5, '123');
Cela identifie automatiquement la clé primaire et trouve une ligne correspondante à mettre à jour, en insérant une nouvelle si aucune n'est trouvée.
la source
SQLite prend en charge le remplacement d'une ligne si elle existe déjà:
INSERT OR REPLACE INTO [...blah...]
Vous pouvez raccourcir ceci en
REPLACE INTO [...blah...]
Ce raccourci a été ajouté pour être compatible avec l'
REPLACE INTO
expression MySQL .la source
PRAMARY KEY
dans vos valeurs.Je ferais quelque chose comme ce qui suit:
INSERT INTO cache VALUES (key, generation) ON DUPLICATE KEY UPDATE (key = key, generation = generation + 1);
Définition de la valeur de génération à 0 dans le code ou dans le sql mais en utilisant ON DUP ... pour incrémenter la valeur. Je pense que c'est la syntaxe de toute façon.
la source
la clause ON DUPLICATE KEY UPDATE est la meilleure solution car: REPLACE effectue un DELETE suivi d'un INSERT donc pendant une période très courte, l'enregistrement est supprimé, ce qui crée la très faible possibilité qu'une requête puisse revenir après avoir ignoré cela si la page était visualisé lors de la requête REMPLACER.
Je préfère INSÉRER ... SUR LA MISE À JOUR DUPLICATE ... pour cette raison.
La solution de jmoz est la meilleure: bien que je préfère la syntaxe SET aux parenthèses
INSERT INTO cache SET key = 'key', generation = 'generation' ON DUPLICATE KEY UPDATE key = 'key', generation = (generation + 1) ;
la source
Je ne sais pas si vous allez trouver une solution neutre en termes de plate-forme.
Ceci est communément appelé un «UPSERT».
Voir quelques discussions connexes:
la source
Dans PostgreSQL il n'y a pas de commande de fusion, et en fait l'écrire n'est pas trivial - il y a en fait des cas de bord étranges qui rendent la tâche "intéressante".
La meilleure approche (comme dans: travailler dans les conditions les plus possibles) est d'utiliser une fonction - telle que celle illustrée dans le manuel (merge_db).
Si vous ne souhaitez pas utiliser la fonction, vous pouvez généralement vous en sortir avec:
updated = db.execute(UPDATE ... RETURNING 1) if (!updated) db.execute(INSERT...)
Rappelez - vous qu'il est pas faute de preuve et il va échouer par la suite.
la source
Le SQL standard fournit l'instruction MERGE pour cette tâche. Tous les SGBD ne prennent pas en charge l'instruction MERGE.
la source
Si vous n'avez pas de moyen commun de mettre à jour ou d'insérer atomiquement (par exemple, via une transaction), vous pouvez revenir à un autre schéma de verrouillage. Un fichier de 0 octet, un mutex système, un tube nommé, etc ...
la source
Pourriez-vous utiliser un déclencheur d'insertion? En cas d'échec, effectuez une mise à jour.
la source
Si vous êtes d'accord avec l'utilisation d'une bibliothèque qui écrit le SQL pour vous, vous pouvez utiliser Upsert (actuellement Ruby et Python uniquement):
Cela fonctionne avec MySQL, Postgres et SQLite3.
Il écrit une procédure stockée ou une fonction définie par l'utilisateur (UDF) dans MySQL et Postgres. Il utilise
INSERT OR REPLACE
dans SQLite3.la source