59 votes

Mise à jour si différent/changement

Est-il possible d'exécuter une déclaration de mise à jour en sql, mais de ne mettre à jour que si les mises à jour sont différentes ?

par exemple

si dans la base de données, col1 = "hello"

update table1 set col1 = 'hello'

ne devrait pas effectuer tout type de mise à jour

toutefois, si

update table1 set col1 = "bye"

ce debe effectuer une mise à jour.

69voto

Dude0001 Points 910

Lors de la compilation et de l'exécution des requêtes, le serveur SQL ne prend pas le temps de déterminer si une instruction UPDATE va effectivement modifier des valeurs ou non. Il effectue simplement les écritures comme prévu, même si elles ne sont pas nécessaires.

Dans un scénario comme

update table1 set col1 = 'hello'

vous pourriez penser que SQL ne fera rien, mais il le fera - il effectuera toutes les écritures nécessaires comme si vous aviez réellement modifié la valeur. Cela se produit à la fois pour la table physique (ou l'index clusterisé) et pour tout index non clusterisé défini sur cette colonne. Cela entraîne des écritures dans les tables/index physiques, le recalcul des index et des écritures dans le journal des transactions. Lorsque l'on travaille avec de grands ensembles de données, il y a d'énormes avantages en termes de performances à ne mettre à jour que les lignes qui recevront un changement.

Si nous voulons éviter le surcoût de ces écritures lorsque cela n'est pas nécessaire, nous devons trouver un moyen de vérifier la nécessité d'une mise à jour. Une façon de vérifier la nécessité d'une mise à jour serait d'ajouter quelque chose comme "where col <> 'hello'.

update table1 set col1 = 'hello' where col1 <> 'hello'

Mais cette méthode n'est pas très efficace dans certains cas, par exemple si vous mettez à jour plusieurs colonnes dans un tableau comportant de nombreuses lignes et que seul un petit sous-ensemble de ces lignes voit ses valeurs modifiées. Cela est dû à la nécessité de filtrer ensuite sur toutes ces colonnes, et les prédicats de non-égalité ne sont généralement pas en mesure d'utiliser les recherches d'index, et la surcharge des écritures de table et d'index et des entrées de journal de transaction comme mentionné ci-dessus.

Mais il existe une bien meilleure solution, qui consiste à combiner une clause EXISTS et une clause EXCEPT. L'idée est de comparer les valeurs de la ligne de destination à celles de la ligne source correspondante pour déterminer si une mise à jour est réellement nécessaire. Regardez la requête modifiée ci-dessous et examinez le filtre de requête supplémentaire commençant par EXISTS. Notez qu'à l'intérieur de la clause EXISTS, les instructions SELECT n'ont pas de clause FROM. Cette partie est particulièrement importante car cela ne fait qu'ajouter un balayage constant et une opération de filtrage supplémentaires dans le plan de requête (le coût des deux est trivial). Vous obtenez ainsi une méthode très légère pour déterminer si une mise à jour est nécessaire en premier lieu, en évitant les surcharges d'écriture inutiles.

update table1 set col1 = 'hello'
/* AVOID NET ZERO CHANGES */
where exists 
    (
    /* DESTINATION */
    select table1.col1
    except
    /* SOURCE */
    select col1 = 'hello'
    )

Cela semble excessivement compliqué par rapport à la vérification des mises à jour dans une simple clause WHERE pour le scénario simple de la question initiale, lorsque vous mettez à jour une valeur pour toutes les lignes d'une table avec une valeur littérale. Cependant, cette technique fonctionne très bien si vous mettez à jour plusieurs colonnes dans une table, que la source de votre mise à jour est une autre requête et que vous voulez minimiser les écritures et les entrées dans les journaux de transactions. Elle est également plus performante que le test de chaque champ avec <>.

Un exemple plus complet pourrait être

update table1
   set col1 = 'hello',
       col2 = 'hello',
       col3 = 'hello'
/* Only update rows from CustomerId 100, 101, 102 & 103 */
where table1.CustomerId IN (100, 101, 102, 103)
/* AVOID NET ZERO CHANGES */
  and exists 
    (
    /* DESTINATION */
    select table1.col1
           table1.col2
           table1.col3
    except
    /* SOURCE */
    select z.col1,
           z.col2,
           z.col3
      from #anytemptableorsubquery z
     where z.CustomerId = table1.CustomerId
    )

38voto

sll Points 30638

L'idée est de ne pas effectuer de mise à jour si une nouvelle valeur est la même que dans la base de données actuellement.

WHERE col1 != @newValue

(évidemment, il y a aussi des Id pour identifier une ligne)

WHERE Id = @Id AND col1 != @newValue

PS : A l'origine, vous voulez faire une mise à jour uniquement si la valeur est 'bye', alors ajoutez simplement AND col1 = 'bye' mais j'ai l'impression que c'est redondant, je suppose juste que

15voto

ypercube Points 62714

Si vous voulez changer le champ en 'hello' seulement si c'est 'bye' utilisez ceci :

UPDATE table1
SET col1 = 'hello'
WHERE col1 = 'bye'

Si vous voulez mettre à jour seulement si elle est différente que 'hello' utiliser :

UPDATE table1
SET col1 = 'hello'
WHERE col1 <> 'hello'

Y a-t-il une raison à cette étrange approche ? Comme Daniel l'a commenté, il n'y a pas de gain particulier - sauf peut-être si vous avez des milliers de lignes avec col1='hello' . Est-ce le cas ?

10voto

Daniel Hilgarth Points 90722

Cela est possible grâce à un déclencheur before-update. Dans ce déclencheur, vous pouvez comparer les anciennes et les nouvelles valeurs et annuler la mise à jour si elles ne diffèrent pas. Mais cela entraînera alors une erreur sur le site de l'appelant.
Je ne sais pas pourquoi vous voulez faire ça, mais il y a plusieurs possibilités :

  1. Performance : Il n'y a pas de gain de performance ici, car la mise à jour devrait non seulement trouver la bonne ligne mais aussi comparer les données.
  2. Déclencheur : Si vous voulez que le déclencheur ne soit déclenché que s'il y a eu un réel changement, vous devez implémenter votre déclencheur de telle sorte qu'il compare toutes les anciennes valeurs aux nouvelles avant de faire quoi que ce soit.

2voto

Vous avez besoin d'une clé unique id dans votre tableau, (supposons que sa valeur soit 1) pour faire quelque chose comme :

UPDATE table1 SET col1="hello" WHERE id=1 AND col1!="hello"

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X