Database
 sql >> Base de données >  >> RDS >> Database

Vérifier si une colonne non LOB doit être mise à jour

Parfois, je vois des gens essayer "d'optimiser" leurs instructions de mise à jour pour éviter d'écrire la même valeur dans une colonne particulière. Ma compréhension a toujours été que si vous allez mettre à jour une ligne, en supposant que toutes les valeurs sont dans la ligne, les coûts de verrouillage de la ligne sont beaucoup plus élevés que le coût supplémentaire de la mise à jour d'une, deux ou toutes les colonnes de cette ligne .

J'ai donc créé un tableau simple pour tester ceci :

CREATE TABLE dbo.whatever( ID INT IDENTITY(1,1) PRIMARY KEY, v1 NVARCHAR(50) NOT NULL, v2 NVARCHAR(50) NOT NULL, v3 NVARCHAR(50) NOT NULL, v4 NVARCHAR(50) NOT NULL, v5 NVARCHAR(50) PAS NULL, v6 NVARCHAR(50) PAS NULL);

Ensuite, j'ai créé une procédure stockée pour remplir la table avec 50 000 lignes avec une variété de petites chaînes :

CREATE PROCEDURE dbo.cleanASBEGIN SET NOCOUNT ON ; TRUNCATE TABLE dbo.whatever;;AVEC x(d) AS ( SELECT d FROM ( VALEURS (N'abc'),(N'def'),(N'ghi'), (N'jkl'),(N'mno'),(N 'pqr') ) AS y(d) ) INSERT dbo.whatever(v1, v2, v3, v4, v5, v6) SELECT TOP (50000) x1.d, x2.d, x3.d, x4.d, x5 .d, x6.d DE x AS x1, x AS x2, x AS x3, x AS x4, x AS x5, x AS x6, x AS x7;ENDGO

Ensuite, j'ai écrit des instructions de mise à jour formulées de deux manières que vous pourriez "éviter" d'écrire dans une colonne spécifique, étant donné cette affectation de variable :

DÉCLARER @v1 NVARCHAR(50) =N'abc', @v2 NVARCHAR(50) =N'def', @v3 NVARCHAR(50) =N'ghi', @v4 NVARCHAR(50) =N'jkl ', @v5 NVARCHAR(50) =N'mno', @v6 NVARCHAR(50) =N'pqr';

D'abord en utilisant une expression CASE pour vérifier si la valeur de la colonne est la même que la valeur de la variable :

UPDATE dbo.whatever SET v1 =CASE WHEN v1 <> @v1 ​​THEN @v1 ELSE v1 END, v2 =CASE WHEN v2 <> @v2 THEN @v2 ELSE v2 END, v3 =CASE WHEN v3 <> @v3 THEN @v3 SINON v3 FIN, v4 =CAS QUAND v4 <> @v4 ALORS @v4 SINON v4 FIN, v5 =CAS QUAND v5 <> @v5 ALORS @v5 SINON v5 FIN, v6 =CAS QUAND v6 <> @v6 ALORS @v6 ELSE v6 ENDWHERE( v1 <> @v1 ​​OU v2 <> @v2 OU v3 <> @v3 OU v4 <> @v4 OU v5 <> @v5 OU v6 <> @v6);

Et deuxièmement en émettant une MISE À JOUR indépendante pour chaque colonne (chacune ciblant uniquement les lignes où cette valeur a, en fait, changé) :

UPDATE dbo.whatever SET v1 =@v1 WHERE v1 <> @v1;UPDATE dbo.whatever SET v2 =@v2 WHERE v2 <> @v2;UPDATE dbo.whatever SET v3 =@v3 WHERE v3 <> @v3;UPDATE dbo.whatever SET v4 =@v4 WHERE v4 <> @v4;UPDATE dbo.whatever SET v5 =@v5 WHERE v5 <> @v5;UPDATE dbo.whatever SET v6 =@v6 WHERE v6 <> @v6; 

Ensuite, je comparerais cela à la façon dont la plupart d'entre nous le feraient aujourd'hui :il suffit de METTRE À JOUR toutes les colonnes sans se soucier de savoir s'il s'agissait de la valeur préexistante pour cette colonne particulière :

UPDATE dbo.whatever SET v1 =@v1, v2 =@v2, v3 =@v3, v4 =@v4, v5 =@v5, v6 =@v6WHERE( v1 <> @v1 ​​OU v2 <> @v2 OU v3 <> @v3 OU v4 <> @v4 OU v5 <> @v5 OU v6 <> @v6);

(Ceux-ci supposent tous que les colonnes et les paramètres/variables ne sont pas NULLables - ils devraient utiliser COALESCE pour tenir compte de la comparaison des valeurs NULL de chaque côté si tel est le cas. Ils supposent également que vous auriez une clause WHERE supplémentaire pour cibler des lignes spécifiques - dans cet exemple, vous pouvez exécuter les première et troisième requêtes sans la clause WHERE globale et voir des résultats presque identiques. J'ai gardé cela simple pour plus de concision.)

Ensuite, j'ai voulu voir ce qui se passait dans ces trois cas lorsque n'importe quelle valeur pouvait être modifiée, lorsque des valeurs particulières pouvaient être modifiées, lorsqu'aucune valeur ne serait modifiée et lorsque toutes les valeurs seraient modifiées. Je pourrais modifier cela en modifiant la procédure stockée pour insérer des constantes dans des colonnes particulières, ou en modifiant la façon dont les variables étaient affectées.

-- pour indiquer quand une valeur peut changer dans une ligne, la procédure utilise la jointure croisée complète :SELECT TOP (50000) x1.d, x2.d, x3.d, x4.d, x5.d, x6 .d -- pour montrer quand des valeurs particulières changeront sur plusieurs lignes, nous pouvons coder en dur des constantes :-- deux valeurs exemptes :SELECT TOP (50000) N'abc', N'def', x3.d, x4.d , x5.d, x6.d -- quatre valeurs exemptes :SELECT TOP (50000) N'abc', N'def', N'ghi', N'jkl', x5.d, x6.d -- pour afficher lorsqu'aucune valeur ne change, nous codons en dur les six valeurs :SELECT TOP (50000) N'abc', N'def', N'ghi', N'jkl', N'mno', N'pqr' -- et pour montrer quand toutes les valeurs changeront, une affectation de variable différente aurait lieu :DECLARE @v1 NVARCHAR(50) =N'zzz', @v2 NVARCHAR(50) =N'zzz', @v3 NVARCHAR(50) =N 'zzz', @v4 NVARCHAR(50) =N'zzz', @v5 NVARCHAR(50) =N'zzz', @v6 NVARCHAR(50) =N'zzz';

Résultats

Après avoir exécuté ces tests, la "mise à jour aveugle" a gagné dans chaque scénario. Maintenant, vous pensez, qu'est-ce que quelques centaines de millisecondes ? Extrapoler. Si vous effectuez de nombreuses mises à jour dans votre système, cela peut vraiment commencer à avoir des conséquences.

Résultats détaillés dans Plan Explorer :Tout changement | 2 valeurs exemptées | 4 valeurs exemptées | Toutes les valeurs exemptées | Tout changer

Sur la base des commentaires de Roji, j'ai décidé de tester également cela avec quelques index :

CREATE INDEX x1 ON dbo.whatever(v1);CREATE INDEX x2 ON dbo.whatever(v2);CREATE INDEX x3 ON dbo.whatever(v3) INCLUDE(v4,v5,v6);

Les durées ont été considérablement augmentées avec ces index :

Résultats détaillés dans Plan Explorer :Tout changement | 2 valeurs exemptées | 4 valeurs exemptées | Toutes les valeurs exemptées | Tout changer

Conclusion

D'après ce test, il me semble qu'il ne vaut généralement pas la peine de vérifier si une valeur doit être mise à jour. Si votre instruction UPDATE affecte plusieurs colonnes, il est presque toujours moins cher pour vous d'analyser toutes les colonnes où une valeur peut avoir changé plutôt que de vérifier chaque colonne individuellement. Dans un prochain article, j'examinerai si ce scénario est mis en parallèle pour les colonnes LOB.