Supposons une structure de table de MyTable(KEY, datafield1, datafield2...)
.
Je souhaite souvent mettre à jour un enregistrement existant ou insérer un nouvel enregistrement s'il n'existe pas.
Essentiellement:
IF (key exists)
run update command
ELSE
run insert command
Quelle est la meilleure façon d'écrire cela?
Réponses:
n'oubliez pas les transactions. Les performances sont bonnes, mais une approche simple (SI EXISTE ..) est très dangereuse.
Lorsque plusieurs threads tentent d'effectuer une insertion ou une mise à jour, vous pouvez facilement obtenir une violation de clé primaire.
Les solutions fournies par @Beau Crawford et @Esteban montrent une idée générale mais sujette aux erreurs.
Pour éviter les blocages et les violations de PK, vous pouvez utiliser quelque chose comme ceci:
ou
la source
Voir ma réponse détaillée à une question précédente très similaire
@Beau Crawford est un bon moyen dans SQL 2005 et ci-dessous, bien que si vous accordez un représentant, il devrait aller au premier gars pour le faire . Le seul problème est que pour les insertions, il s'agit toujours de deux opérations d'E / S.
MS SQL2008 introduit à
merge
partir de la norme SQL: 2003:Maintenant, c'est vraiment juste une opération IO, mais un code affreux :-(
la source
upsert
celle que presque tous les autres fournisseurs de bases de données ont décidé de prendre en charge à la place. Laupsert
syntaxe est un moyen beaucoup plus agréable de le faire, donc à tout le moins, MS aurait dû le prendre en charge également - ce n'est pas comme si c'était le seul mot-clé non standard dans T-SQLMERGE
syntaxe.HOLDLOCK
opération de fusion dans des situations de concurrence élevée.Faites un UPSERT:
http://en.wikipedia.org/wiki/Upsert
la source
Beaucoup de gens vous proposeront de l'utiliser
MERGE
, mais je vous déconseille. Par défaut, il ne vous protège pas plus de la concurrence et des conditions de concurrence que de multiples déclarations, et il présente d'autres dangers:http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/
Même avec cette syntaxe "plus simple" disponible, je préfère toujours cette approche (gestion des erreurs omise pour plus de brièveté):
Beaucoup de gens vont suggérer de cette façon:
Mais tout cela permet de s'assurer que vous devrez peut-être lire le tableau deux fois pour localiser la ou les lignes à mettre à jour. Dans le premier exemple, vous n'aurez besoin de localiser les lignes qu'une seule fois. (Dans les deux cas, si aucune ligne n'est trouvée lors de la lecture initiale, une insertion se produit.)
D'autres proposeront de cette façon:
Cependant, cela est problématique si, pour aucune autre raison que de laisser SQL Server intercepter les exceptions que vous auriez pu empêcher en premier lieu, c'est beaucoup plus cher, sauf dans le rare scénario où presque chaque insertion échoue. J'en prouve autant ici:
la source
UPDATE target SET col = tmp.col FROM target INNER JOIN #tmp ON <key clause>; INSERT target(...) SELECT ... FROM #tmp AS t WHERE NOT EXISTS (SELECT 1 FROM target WHERE key = t.key);
Éditer:
Hélas, même à mon détriment, je dois admettre que les solutions qui le font sans sélection semblent être meilleures car elles accomplissent la tâche avec une étape de moins.
la source
Si vous souhaitez UPSERT plusieurs enregistrements à la fois, vous pouvez utiliser l'instruction MERGE ANSI SQL: 2003 DML.
Consultez Mimicking MERGE Statement dans SQL Server 2005 .
la source
Bien qu'il soit assez tard pour commenter cela, je veux ajouter un exemple plus complet en utilisant MERGE.
Ces instructions Insert + Update sont généralement appelées instructions "Upsert" et peuvent être implémentées à l'aide de MERGE dans SQL Server.
Un très bon exemple est donné ici: http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
Ce qui précède explique également les scénarios de verrouillage et de concurrence.
Je citerai la même chose pour référence:
la source
Remplacez les noms de table et de champ par tout ce dont vous avez besoin. Prenez soin de l' utilisation de la condition ON . Définissez ensuite la valeur appropriée (et le type) pour les variables sur la ligne DECLARE.
À votre santé.
la source
Vous pouvez utiliser
MERGE
Statement, This statement is used to insert data if not exist or update if does exist.la source
Si vous passez à la route UPDATE if-no-rows-updated puis INSERT, envisagez de faire l'insertion en premier pour éviter une condition de concurrence (en supposant qu'aucune intervention ne soit SUPPRIMÉE)
En plus d'éviter une condition de concurrence, si dans la plupart des cas l'enregistrement existe déjà, cela entraînera l'échec de INSERT, gaspillant le processeur.
L'utilisation de MERGE est probablement préférable pour SQL2008 et ultérieur.
la source
Cela dépend du modèle d'utilisation. Il faut regarder la situation dans son ensemble sans se perdre dans les détails. Par exemple, si le modèle d'utilisation est de 99% de mises à jour après la création de l'enregistrement, le «UPSERT» est la meilleure solution.
Après la première insertion (hit), ce sera toutes les mises à jour de déclaration unique, pas de si ou de mais. La condition «où» sur l'insert est nécessaire, sinon elle insérera des doublons et vous ne voulez pas vous occuper du verrouillage.
la source
MS SQL Server 2008 présente la déclaration MERGE, qui, je crois, fait partie de la norme SQL: 2003. Comme beaucoup l'ont montré, ce n'est pas un gros problème de gérer des cas sur une ligne, mais quand il s'agit de grands ensembles de données, il faut un curseur, avec tous les problèmes de performances qui en découlent. La déclaration MERGE sera très appréciée pour les grands ensembles de données.
la source
Avant que tout le monde passe à HOLDLOCK-s par peur de ces utilisateurs nafariés exécutant directement vos sprocs :-) permettez-moi de souligner que vous devez garantir l'unicité des nouveaux PK-s par conception (clés d'identité, générateurs de séquence dans Oracle, index uniques pour ID-s externes, requêtes couvertes par des index). C'est l'alpha et l'oméga du problème. Si vous ne l'avez pas, aucun HOLDLOCK de l'univers ne vous sauvera et si vous l'avez, alors vous n'avez besoin de rien au-delà de UPDLOCK sur la première sélection (ou d'utiliser la mise à jour en premier).
Les Sprocs fonctionnent normalement dans des conditions très contrôlées et avec l'hypothèse d'un appelant de confiance (niveau intermédiaire). Cela signifie que si un modèle simple d'insertion (mise à jour + insertion ou fusion) voit un PK en double, cela signifie un bogue dans votre conception de niveau intermédiaire ou de table et il est bon que SQL crie une faute dans un tel cas et rejette l'enregistrement. Placer un HOLDLOCK dans ce cas équivaut à manger des exceptions et à prendre des données potentiellement défectueuses, en plus de réduire votre perf.
Cela dit, l'utilisation de MERGE ou UPDATE puis INSERT est plus facile sur votre serveur et moins sujette aux erreurs car vous n'avez pas à vous rappeler d'ajouter (UPDLOCK) pour sélectionner d'abord. De plus, si vous effectuez des insertions / mises à jour en petits lots, vous devez connaître vos données afin de décider si une transaction est appropriée ou non. Si c'est juste une collection de documents non liés, une transaction "enveloppante" supplémentaire sera préjudiciable.
la source
Les conditions de course sont-elles vraiment importantes si vous essayez d'abord une mise à jour suivie d'un encart? Disons que vous avez deux threads qui veulent définir une valeur pour la clé clé :
Fil 1: valeur = 1
Fil 2: valeur = 2
Exemple de scénario de conditions de concurrence
L'autre thread échoue avec l'insertion (avec une clé en double d'erreur) - thread 2.
Mais; dans un environnement multithread, le planificateur du système d'exploitation décide de l'ordre d'exécution des threads - dans le scénario ci-dessus, où nous avons cette condition de concurrence critique, c'est le système d'exploitation qui a décidé de la séquence d'exécution. C'est à dire: Il est faux de dire que "thread 1" ou "thread 2" était "premier" du point de vue du système.
Lorsque le temps d'exécution est si proche pour le thread 1 et le thread 2, le résultat de la condition de concurrence n'a pas d'importance. La seule exigence doit être que l'un des threads définisse la valeur résultante.
Pour la mise en œuvre: Si la mise à jour suivie de l'insertion entraîne une erreur "clé en double", cela doit être traité comme un succès.
De plus, il ne faut bien sûr jamais supposer que la valeur dans la base de données est la même que la valeur que vous avez écrite en dernier.
la source
Dans SQL Server 2008, vous pouvez utiliser l'instruction MERGE
la source
J'avais essayé la solution ci-dessous et cela fonctionne pour moi, lorsqu'une demande simultanée d'insertion se produit.
la source
Vous pouvez utiliser cette requête. Fonctionne dans toutes les éditions de SQL Server. C'est simple et clair. Mais vous devez utiliser 2 requêtes. Vous pouvez utiliser si vous ne pouvez pas utiliser MERGE
REMARQUE: veuillez expliquer les réponses négatives
la source
Si vous utilisez ADO.NET, le DataAdapter gère cela.
Si vous voulez le gérer vous-même, voici la façon:
Assurez-vous qu'il existe une contrainte de clé primaire sur votre colonne de clé.
Alors vous:
Vous pouvez également le faire dans l'autre sens, c'est-à-dire faire l'insertion en premier et effectuer la mise à jour si l'insertion échoue. Normalement, la première façon est meilleure, car les mises à jour sont effectuées plus souvent que les insertions.
la source
Faire un if existe ... sinon ... implique de faire deux requêtes minimum (une pour vérifier, une pour agir). L'approche suivante nécessite un seul où l'enregistrement existe, deux si une insertion est requise:
la source
Je fais habituellement ce que plusieurs des autres affiches ont dit en ce qui concerne la vérification de son existence en premier, puis en faisant le bon chemin. Une chose que vous devez retenir lorsque vous effectuez cette opération est que le plan d'exécution mis en cache par sql peut être non optimal pour un chemin ou l'autre. Je crois que la meilleure façon de procéder consiste à appeler deux procédures stockées différentes.
Maintenant, je ne suis pas très souvent mon propre conseil, alors prenez-le avec un grain de sel.
la source
Faites un choix, si vous obtenez un résultat, mettez-le à jour, sinon, créez-le.
la source