Si j'ai une instruction UPDATE qui ne modifie en fait aucune donnée (car les données sont déjà à l'état mis à jour), y a-t-il un avantage en termes de performances à mettre une vérification dans la clause where pour empêcher la mise à jour?
Il pourrait certainement y en avoir car il y a une légère différence de performances due à la MISE À JOUR 1 :
- pas de mise à jour des lignes (donc rien à écrire sur le disque, pas même une activité minimale du journal), et
- supprimer les verrous moins restrictifs que ce qui est nécessaire pour effectuer la mise à jour réelle (donc mieux pour la concurrence) ( veuillez consulter la section Mise à jour vers la fin )
Cependant, vous devez mesurer la différence qu'il y a sur votre système avec votre schéma, vos données et votre charge système. Plusieurs facteurs jouent sur l'impact d'une MISE À JOUR sans mise à jour:
- la quantité de conflits sur la table en cours de mise à jour
- le nombre de lignes mises à jour
- s'il y a des déclencheurs UPDATE sur la table en cours de mise à jour (comme l'a noté Mark dans un commentaire sur la question). Si vous exécutez
UPDATE TableName SET Field1 = Field1
, un déclencheur de mise à jour se déclenchera et indiquera que le champ a été mis à jour (si vous vérifiez à l'aide des fonctions UPDATE () ou COLUMNS_UPDATED ), et que le champ dans les deux INSERTED
et les DELETED
tables ont la même valeur.
En outre, la section récapitulative suivante se trouve dans l'article de Paul White, L'impact des mises à jour sans mise à jour (comme l'a noté @spaghettidba dans un commentaire sur sa réponse):
SQL Server contient un certain nombre d'optimisations pour éviter la journalisation inutile ou le vidage de page lors du traitement d'une opération UPDATE qui n'entraînera aucune modification de la base de données persistante.
- Les mises à jour sans mise à jour d'une table en cluster évitent généralement la journalisation supplémentaire et le vidage de page, sauf si une colonne qui forme (une partie de) la clé de cluster est affectée par l'opération de mise à jour.
- Si une partie de la clé de cluster est «mise à jour» à la même valeur, l'opération est enregistrée comme si les données avaient changé et les pages affectées sont marquées comme sales dans le pool de mémoire tampon. Ceci est une conséquence de la conversion de la MISE À JOUR en une opération de suppression puis d'insertion.
- Les tables de tas se comportent de la même manière que les tables en cluster, sauf qu'elles n'ont pas de clé de cluster pour provoquer une journalisation supplémentaire ou un vidage de page. Cela reste le cas même lorsqu'une clé primaire non clusterisée existe sur le tas. Les mises à jour non mises à jour d'un tas évitent donc généralement la journalisation et le vidage supplémentaires (mais voir ci-dessous).
- Les tas et les tables en cluster subiront la journalisation et le vidage supplémentaires pour toute ligne où une colonne LOB contenant plus de 8000 octets de données est mise à jour à la même valeur en utilisant une syntaxe autre que 'SET nom_colonne = nom_colonne'.
- L'activation simple de l'un ou l'autre type de niveau d'isolation de version de ligne sur une base de données entraîne toujours la journalisation et le vidage supplémentaires. Cela se produit quel que soit le niveau d'isolement en vigueur pour la transaction de mise à jour.
Veuillez garder à l'esprit (surtout si vous ne suivez pas le lien pour voir l'article complet de Paul), les deux éléments suivants:
Les mises à jour non mises à jour ont toujours une certaine activité de journal, indiquant qu'une transaction commence et se termine. C'est juste qu'aucune modification des données ne se produit (ce qui est toujours une bonne économie).
Comme je l'ai indiqué ci-dessus, vous devez tester sur votre système. Utilisez les mêmes requêtes de recherche que Paul utilise et voyez si vous obtenez les mêmes résultats. Je vois des résultats légèrement différents sur mon système que ce qui est indiqué dans l'article. Toujours pas de pages sales à écrire, mais un peu plus d'activité de journal.
... J'ai besoin du nombre de lignes pour inclure la ligne inchangée, donc je sais s'il faut faire une insertion si l'ID n'existe pas. ... est-il possible d'obtenir le nombre de lignes dont j'ai besoin?
Simplement, si vous ne traitez qu'avec une seule ligne, vous pouvez effectuer les opérations suivantes:
UPDATE MyTable
SET Value = 2
WHERE ID = 2
AND Value <> 2;
IF (@@ROWCOUNT = 0)
BEGIN
IF (NOT EXISTS(
SELECT *
FROM MyTable
WHERE ID = 2 -- or Value = 2 depending on the scenario
)
)
BEGIN
INSERT INTO MyTable (ID, Value) -- or leave out ID if it is an IDENTITY
VALUES (2, 2);
END;
END;
Pour plusieurs lignes, vous pouvez obtenir les informations nécessaires pour prendre cette décision en utilisant la OUTPUT
clause. En capturant exactement quelles lignes ont été mises à jour, vous pouvez affiner les éléments à rechercher pour connaître la différence entre la non-mise à jour des lignes qui n'existent pas et la non-mise à jour des lignes qui existent mais n'ont pas besoin de la mise à jour.
Je montre l'implémentation de base dans la réponse suivante:
Comment éviter d'utiliser la requête de fusion lors de la migration de plusieurs données à l'aide du paramètre xml?
La méthode indiquée dans cette réponse ne filtre pas les lignes qui existent mais n'ont pas besoin d'être mises à jour. Cette partie pourrait être ajoutée, mais vous devez d'abord montrer exactement où vous obtenez votre ensemble de données dans lequel vous fusionnez MyTable
. Viennent-ils d'une table temporaire? Un paramètre table (TVP)?
MISE À JOUR 1:
J'ai finalement pu faire quelques tests et voici ce que j'ai trouvé concernant le journal des transactions et le verrouillage. Tout d'abord, le schéma de la table:
CREATE TABLE [dbo].[Test]
(
[ID] [int] NOT NULL CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED,
[StringField] [varchar](500) NULL
);
Ensuite, le test met à jour le champ à la valeur qu'il a déjà:
UPDATE rt
SET rt.StringField = '04CF508B-B78E-4264-B9EE-E87DC4AD237A'
FROM dbo.Test rt
WHERE rt.ID = 4082117
Résultats:
-- Transaction Log (2 entries):
Operation
----------------------------
LOP_BEGIN_XACT
LOP_COMMIT_XACT
-- SQL Profiler (3 Lock:Acquired events):
Mode Type
--------------------------------------
8 - IX 5 - OBJECT
8 - IX 6 - PAGE
5 - X 7 - KEY
Enfin, le test qui filtre la mise à jour car la valeur ne change pas:
UPDATE rt
SET rt.StringField = '04CF508B-B78E-4264-B9EE-E87DC4AD237A'
FROM dbo.Test rt
WHERE rt.ID = 4082117
AND rt.StringField <> '04CF508B-B78E-4264-B9EE-E87DC4AD237A';
Résultats:
-- Transaction Log (0 entries):
Operation
----------------------------
-- SQL Profiler (3 Lock:Acquired events):
Mode Type
--------------------------------------
8 - IX 5 - OBJECT
7 - IU 6 - PAGE
4 - U 7 - KEY
Comme vous pouvez le voir, rien n'est écrit dans le journal des transactions lors du filtrage de la ligne, contrairement aux deux entrées marquant le début et la fin de la transaction. Et s'il est vrai que ces deux entrées ne sont presque rien, elles sont toujours quelque chose.
De plus, le verrouillage des ressources PAGE et KEY est moins restrictif lors du filtrage des lignes qui n'ont pas changé. Si aucun autre processus n'interagit avec cette table, il s'agit probablement d'un problème (mais quelle est la probabilité, vraiment?). Gardez à l'esprit que ce test affiché dans l'un des blogs liés (et même mes tests) suppose implicitement qu'il n'y a pas de conflit sur la table car il ne fait jamais partie des tests. Dire que les mises à jour non mises à jour sont si légères qu'elles ne paient pas pour faire le filtrage doit être pris avec un grain de sel puisque le test a été fait, plus ou moins, dans le vide. Mais en production, ce tableau n'est probablement pas isolé. Bien sûr, il se pourrait très bien que le peu de journalisation et les verrous plus restrictifs ne se traduisent pas par une efficacité moindre. Alors, la source d'information la plus fiable pour répondre à cette question? Serveur SQL. Plus précisément:votre serveur SQL. Il vous montrera quelle méthode est la meilleure pour votre système :-).
MISE À JOUR 2:
Si les opérations dans lesquelles la nouvelle valeur est identique à la valeur actuelle (c.-à-d. Pas de mise à jour) sortent le nombre d'opérations dans lesquelles la nouvelle valeur est différente et la mise à jour est nécessaire, alors le modèle suivant pourrait s'avérer encore meilleur, surtout si il y a beaucoup de conflits sur la table. L'idée est de faire un SELECT
premier simple pour obtenir la valeur actuelle. Si vous n'obtenez pas de valeur, vous avez votre réponse concernant le INSERT
. Si vous avez une valeur, vous pouvez faire un simple IF
et émettre le UPDATE
seul si c'est nécessaire.
DECLARE @CurrentValue VARCHAR(500) = NULL,
@NewValue VARCHAR(500) = '04CF508B-B78E-4264-B9EE-E87DC4AD237A',
@ID INT = 4082117;
SELECT @CurrentValue = rt.StringField
FROM dbo.Test rt
WHERE rt.ID = @ID;
IF (@CurrentValue IS NULL) -- if NULL is valid, use @@ROWCOUNT = 0
BEGIN
-- row does not exist
INSERT INTO dbo.Test (ID, StringField)
VALUES (@ID, @NewValue);
END;
ELSE
BEGIN
-- row exists, so check value to see if it is different
IF (@CurrentValue <> @NewValue)
BEGIN
-- value is different, so do the update
UPDATE rt
SET rt.StringField = @NewValue
FROM dbo.Test rt
WHERE rt.ID = @ID;
END;
END;
Résultats:
-- Transaction Log (0 entries):
Operation
----------------------------
-- SQL Profiler (2 Lock:Acquired events):
Mode Type
--------------------------------------
6 - IS 5 - OBJECT
6 - IS 6 - PAGE
Il n'y a donc que 2 verrous acquis au lieu de 3, et ces deux verrous sont partagés intentionnellement, pas Intention eXclusive ou mise à jour d'intention ( compatibilité de verrouillage ). En gardant à l'esprit que chaque verrou acquis sera également libéré, chaque verrou est en réalité 2 opérations, donc cette nouvelle méthode est un total de 4 opérations au lieu des 6 opérations de la méthode initialement proposée. Considérant que cette opération s'exécute une fois toutes les 15 ms (environ, comme indiqué par l'OP), soit environ 66 fois par seconde. Ainsi, la proposition initiale équivaut à 396 opérations de verrouillage / déverrouillage par seconde, alors que cette nouvelle méthode ne représente que 264 opérations de verrouillage / déverrouillage par seconde de verrous encore plus légers. Ce n'est pas une garantie de performances impressionnantes, mais cela vaut certainement la peine d'être testé :-).