2008-09-29 3 views
3

Souvent, je veux ajouter une valeur à une table ou mettre à jour la valeur si sa clé existe déjà. Cela peut se faire de plusieurs façons, en supposant une clé primaire ou unique est fixé sur les colonnes « user_id » et « pref_key » dans l'exemple:Y at-il un inconvénient à utiliser aveuglément INSERT dans MySQL?

1. Insérez aveugle, mise à jour en cas de réception d'une clé doublon:

// Try to insert as a new value 
INSERT INTO my_prefs 
(user_id, pref_key, pref_value) 
VALUES (1234, 'show_help', 'true'); 

// If a duplicate-key error occurs run an update query 
UPDATE my_prefs 
SET pref_value = 'true' 
WHERE user_id=1234 AND pref_key='show_help'; 

2. Vérifier l'existence, puis sélectionnez ou mise à jour:

// Check for existence 
SELECT COUNT(*) 
FROM my_prefs 
WHERE user_id=1234 AND pref_key='show_help'; 

// If count is zero, insert 
INSERT INTO my_prefs 
(user_id, pref_key, pref_value) 
VALUES (1234, 'show_help', 'true'); 

// If count is one, update 
UPDATE my_prefs 
SET pref_value = 'true' 
WHERE user_id=1234 AND pref_key='show_help'; 

La première semble être préférable car il faudra qu'une seule requête pour n ew inserts et deux pour une mise à jour, où la deuxième manière nécessitera toujours deux requêtes. Y a-t-il quelque chose qui me manque mais qui ferait une mauvaise idée d'insérer aveuglément?

Répondre

2

Y aura-t-il des INSERTs simultanés sur ces lignes? SUPPRIMER? "ON DUPLICATE" sonne bien (le comportement est exactement ce que vous voulez) à condition que vous n'êtes pas préoccupé par la portabilité des bases de données non-MySQL. L'insertion aveugle semble raisonnable et robuste à condition que les lignes ne soient jamais effacées. (Si le cas INSERT échoue parce que la ligne existe, UPDATE doit réussir car la ligne existe toujours, mais cette hypothèse est fausse si les lignes sont supprimées - vous devrez alors réessayer la logique.) Sur les autres bases de données sans "ON DUPLICATE", vous pourriez envisager une optimisation si vous trouvez que la latence est mauvaise: vous pouvez éviter un aller-retour de la base de données dans le cas déjà existant en mettant cette logique dans une procédure stockée.

La vérification de l'existence est difficile à obtenir s'il y a des INSERT simultanés. Des lignes pourraient être ajoutées entre votre SELECT et votre UPDATE. Les transactions ne vont même pas vraiment aider - je pense même au niveau d'isolement "sérialisable", vous verrez "ne peut pas sérialiser l'accès en raison d'une mise à jour simultanée" des erreurs (ou quel que soit le message d'erreur équivalent MySQL). Vous aurez besoin de réessayer la logique, donc je dirais que la personne qui suggère d'utiliser cette méthode pour éviter la "programmation basée sur les exceptions" est incorrecte, tout comme la personne qui suggère de faire la MISE À JOUR en premier pour la même raison.

0

La première façon est la manière préférée autant que je sache.

4

Personnellement, je ne suis jamais fan de la programmation à base d'exception (une exception attend dans le fonctionnement normal d'une application) et pour moi le second exemple est beaucoup plus facile à lire/maintenable.

Il y a des situations où cela ferait une différence (boucles très serrées par exemple) mais je pense qu'il devrait y avoir une bonne raison d'écrire du code comme celui-ci plutôt que ce soit la valeur par défaut.

+0

Peut-être dans Ruby, mais ce n'est pas vrai dans une base de données. Le surcoût de la vérification dans une table n'est pas comme le coût de la vérification d'un objet en mémoire. Vous êtes en train d'appliquer des normes pour votre langue à une base de données. Les gens de base de données ont la norme contraire pour une raison, il fonctionne beaucoup mieux. –

+0

Ce n'est pas rare. Plus j'en apprends sur le serveur SQL, plus je me rends compte que ma norme pour mon code Oracle serait une idée horrible pour le code SQL Server. Ce n'est pas parce que cela s'applique à un endroit que c'est une bonne idée partout. –

0

Dans votre modèle DAO, vous pouvez avoir un champ id.

  • Si défini sur null/-1/quel que soit, les données n'ont pas été conservées.

  • Lorsque vous persistez (ou récupérer la base de données), définissez à l'identifiant dans la base de données.

  • Votre méthode peut vérifier obstinent l'ID et le transmettre à la mise à jour() ou la mise en œuvre add().

  • Flaws: SORTIR de synchronisation avec la base de données, etc. Je suis sûr qu'il ya plus, mais je ne devrais pas travailler ...

7

Il est la troisième voie MySQL, qui serait le préféré un dans ce SGBDR

INSERT INTO my_prefs 
(user_id, pref_key, pref_value) 
VALUES (1234, 'show_help', 'true') 
ON DUPLICATE KEY 
UPDATE pref_value = 'true' 
+0

Wow! Je ne savais pas que ça existait! –

2

vous pourriez être en mesure d'utiliser REPLACE au lieu, ou si vous utilisez un MySQL plus actuel, vous avez la possibilité d'utiliser « INSERT ... ON DUPLICATE KEY UPDATE »

Le fait que plusieurs personnes aient soulevé cette question rapidement dit «vérifiez toujours les documents MySQL» quand vous avez un problème, car ils sont corrects et, dans de nombreux cas, mènent directement à la solution.

12

un coup d'oeil à la syntaxe Duplicate KEY http://dev.mysql.com/doc/refman/5.0/en/insert-select.html

INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE] 
[INTO] tbl_name [(col_name,...)] 
SELECT ... 
[ ON DUPLICATE KEY UPDATE col_name=expr, ... ] 
+0

C'est terrifiant génial! Je vais couper environ 2 pages de code en ce moment. –

+0

Je ne le savais pas; nous utilisons encore MySQL 4 (car la mise à niveau n'est pas triviale) ce qui ne le fait pas. Il a, et nous utilisons, REPLACE qui fait la même chose mais qui est moins flexible. –

0

Tant que vous utilisez MySQL, vous pouvez utiliser le mot-clé ON DUPLICATE.Par exemple:

INSERT INTO my_prefs (user_id, pref_key, pref_value) VALUES (1234, 'show_help', 'true') 
ON DUPLICATE KEY UPDATE (pref_key, pref_value) VALUES ('show_help', 'true'); 
3

Si vous voulez éviter « l'exception » en insérant peut-être un Doublette et que vous voulez utiliser SQL standard (et votre langue/base de données de programmation renvoie le nombre des lignes mises à jour), puis utilisez les éléments suivants "SQL" - commandes (pseudo-code):

int i = SQL("UPDATE my_prefs ..."); 
if(i==0) { 
    SQL("INSERT INTO my_prefs ..."); 
} 

Cela prend également en compte le fait que - pour la plupart des cas d'utilisation - mises à jour ne se produisent plus souvent que des inserts.

+0

Pourquoi les mises à jour se produisent-elles plus souvent que les insertions? Je parie que les gens sur SO insèrent de nouvelles réponses beaucoup plus souvent puis ils les éditent. –

Questions connexes