web-dev-qa-db-fra.com

Mettre à jour si différent / changé

Est-il possible d'effectuer une instruction de mise à jour en sql, mais uniquement de mettre à jour 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"

ceci devrait effectuer une mise à jour.

30
nami

Cela est possible avec un déclencheur avant mise à jour. Dans ce déclencheur, vous pouvez comparer les anciennes aux 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 voici plusieurs possibilités:

  1. Performances: il n'y a pas de gain de performances ici, car la mise à jour aurait non seulement besoin de trouver la ligne correcte, mais également de comparer les données.
  2. Déclencheur: si vous souhaitez que le déclencheur ne soit déclenché qu'en cas de changement réel, vous devez implémenter votre déclencheur de la sorte, afin qu'il compare toutes les anciennes valeurs aux nouvelles valeurs avant de faire quoi que ce soit.
6
Daniel Hilgarth

L'idée est de ne pas effectuer de mise à jour si une nouvelle valeur est la même que dans DB en ce moment

WHERE col1 != @newValue

(évidemment, il devrait aussi y avoir un champ Id pour identifier une ligne)

WHERE Id = @Id AND col1 != @newValue

PS: à l'origine, vous ne voulez mettre à jour que si la valeur est 'bye', alors ajoutez simplement AND col1 = 'bye', mais je pense que c'est redondant, je suppose

28
sll

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

Dans le scénario comme

update table1 set col1 = 'hello'

vous pensez peut-être 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 cluster) ainsi que pour tous les index non cluster définis sur cette colonne. Cela provoque des écritures dans les tables/index physiques, le recalcul des index et les écritures du journal des transactions. Lorsque vous travaillez 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 une modification.

Si nous voulons éviter la surcharge de ces écritures lorsque cela n'est pas nécessaire, nous devons trouver un moyen de vérifier la nécessité d'être mis à jour. Une façon de vérifier la nécessité d'une mise à jour serait d'ajouter quelque chose comme "où col <> 'bonjour'.

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

Mais cela ne fonctionnerait pas bien dans certains cas, par exemple si vous mettiez à jour plusieurs colonnes dans une table avec plusieurs lignes et que seul un petit sous-ensemble de ces lignes verrait en fait leurs valeurs modifiées. Cela est dû à la nécessité de filtrer ensuite 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 du journal des transactions comme mentionné ci-dessus.

Mais il existe une bien meilleure alternative en utilisant une combinaison d'une clause EXISTS avec une clause EXCEPT. L'idée est de comparer les valeurs de la ligne de destination aux valeurs de la ligne source correspondante pour déterminer si une mise à jour est réellement nécessaire. Examinez la requête modifiée ci-dessous et examinez le filtre de requête supplémentaire commençant par EXISTS. Notez que dans la clause EXISTS, les instructions SELECT n'ont pas de clause FROM. Cette partie est particulièrement importante car cela n'ajoute qu'un balayage constant supplémentaire et une opération de filtrage dans le plan de requête (le coût des deux est trivial). Donc, vous vous retrouvez avec une méthode très légère pour déterminer si une MISE À JOUR est même nécessaire en premier lieu, en évitant une surcharge d'écriture inutile.

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

Cela semble trop compliqué vs vérifier les mises à jour dans une simple clause WHERE pour le scénario simple dans la question d'origine 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 et que la source de votre mise à jour est une autre requête et que vous souhaitez réduire les écritures et les entrées des journaux de transactions. Il fonctionne également mieux que de tester 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
    )
21
Dude0001

Si vous souhaitez modifier le champ en 'hello' uniquement si c'est 'bye', utilisez ceci:

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

Si vous souhaitez mettre à jour uniquement s'il est différent de 'hello', utilisation:

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 spécial - sauf peut-être si vous avez des milliers de lignes avec col1='hello'. Est-ce le cas?

10
ypercubeᵀᴹ
CREATE OR REPLACE PROCEDURE stackoverflow([your_value] IN TYPE) AS
BEGIN
   UPDATE   [your_table] t
     SET t.[your_collumn] = [your_value]
   WHERE t.[your_collumn] != [your_value];
  COMMIT;


EXCEPTION
 [YOUR_EXCEPTION];

END stackoverflow;
3
Tvitmsvleli

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

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

Ancienne question mais aucune des réponses n'adresse correctement les valeurs null.

L'utilisation de <> ou! = Vous posera des problèmes lors de la comparaison des valeurs des différences s'il existe une valeur nulle potentielle dans la nouvelle ou l'ancienne valeur pour une mise à jour en toute sécurité uniquement en cas de modification, utilisez le is distinct from opérateur à Postgres. En savoir plus ici

1
maxTrialfire

sans déclarer une variable ou similaire, il est difficile de se référer à la valeur codée en dur insérée. Si tel est votre objectif, vous devriez envisager la solution de Daniel Hilgarth. Normalement, vous aurez la valeur à portée de main. Dans ce cas, voici comment procéder.

declare @t table (col1 varchar(10))

insert @t values ('hello')
insert @t values ('bye')

declare @newvalueforallrows varchar(10)
set @newvalueforallrows = 'hello'

update @t set col1 = @newvalueforallrows
where col1 <> @newvalueforallrows

-- 1 row updated
0
t-clausen.dk

Je pense que cela devrait faire l'affaire pour toi ...

create trigger [trigger_name] on [table_name]
for insert 
AS declare  @new_val datatype,@id int;
select @new_val = i.column_name from inserted i;
select @id = i.Id from inserted i;
update table_name set column_name = @new_val
where table_name.Id = @id and column_name != @new_val;
0
Rajat Thapar