sql >> Database >  >> RDS >> Database

Controleren of een niet-LOB-kolom moet worden bijgewerkt

Af en toe zie ik mensen proberen hun update-statements te "optimaliseren" om te voorkomen dat dezelfde waarde naar een bepaalde kolom wordt geschreven. Ik heb altijd begrepen dat als je een rij gaat bijwerken, ervan uitgaande dat alle waarden in de rij staan, de kosten voor het vergrendelen van de rij veel hoger zijn dan de incrementele kosten voor het bijwerken van een, twee of alle kolommen in die rij .

Dus heb ik een eenvoudige tabel gemaakt om dit te testen:

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) NOT NULL,
  v6 NVARCHAR(50) NOT NULL
);

Vervolgens heb ik een opgeslagen procedure gemaakt om de tabel te vullen met 50.000 rijen met een verscheidenheid aan kleine tekenreeksen:

CREATE PROCEDURE dbo.clean
AS
BEGIN
  SET NOCOUNT ON;
 
  TRUNCATE TABLE dbo.whatever;
 
  ;WITH x(d) AS
  (
    SELECT d FROM
    (
      VALUES (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
   FROM x AS x1, x AS x2, x AS x3, x AS x4,
        x AS x5, x AS x6, x AS x7;
END
GO

Vervolgens schreef ik update-statements die op twee manieren waren geformuleerd, zodat je het schrijven naar een specifieke kolom zou kunnen 'vermijden', gezien deze variabele toewijzing:

DECLARE
  @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';

Eerst door een CASE-expressie te gebruiken om te controleren of de waarde in de kolom hetzelfde is als de waarde in de variabele:

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 ELSE v3 END,
  v4 = CASE WHEN v4 <> @v4 THEN @v4 ELSE v4 END,
  v5 = CASE WHEN v5 <> @v5 THEN @v5 ELSE v5 END,
  v6 = CASE WHEN v6 <> @v6 THEN @v6 ELSE v6 END
WHERE
(
     v1 <> @v1 OR v2 <> @v2 OR v3 <> @v3 
  OR v4 <> @v4 OR v5 <> @v5 OR v6 <> @v6
);

En ten tweede door een onafhankelijke UPDATE uit te geven voor elke kolom (elk gericht op alleen de rijen waar die waarde in feite was gewijzigd):

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;

Dan zou ik dit vergelijken met de manier waarop de meesten van ons dit vandaag zouden doen:UPDATE alle kolommen gewoon zonder er om te geven of dat de reeds bestaande waarde voor die specifieke kolom was:

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

(Deze gaan er allemaal van uit dat de kolommen en de parameters/variabelen niet NULLable zijn - ze zouden COALESCE moeten gebruiken om rekening te houden met het vergelijken van NULL's aan beide kanten als dat het geval is. Ze gaan er ook van uit dat je een extra WHERE-clausule zou hebben om doel specifieke rijen - in dit voorbeeld kunt u de eerste en derde query uitvoeren zonder de allesomvattende WHERE-component en bijna identieke resultaten zien. Ik heb dit eenvoudig gehouden voor de beknoptheid.)

Vervolgens wilde ik zien wat er in deze drie gevallen gebeurt wanneer een waarde kan worden gewijzigd, wanneer bepaalde waarden kunnen worden gewijzigd, wanneer geen waarden worden gewijzigd en wanneer alle waarden worden gewijzigd. Ik zou dit kunnen beïnvloeden door de opgeslagen procedure te wijzigen om constanten in bepaalde kolommen in te voegen, of door de manier te veranderen waarop variabelen werden toegewezen.

-- to show when any value might change in a row, the procedure uses the full cross join:
 
  SELECT TOP (50000) x1.d, x2.d, x3.d, x4.d, x5.d, x6.d
 
-- to show when particular values will change on many rows, we can hard-code constants:
 
  -- two values exempt:
  SELECT TOP (50000) N'abc', N'def', x3.d, x4.d, x5.d, x6.d
 
  -- four values exempt:
  SELECT TOP (50000) N'abc', N'def', N'ghi', N'jkl', x5.d, x6.d
 
-- to show when no values will change, we hard-code all six values:
 
  SELECT TOP (50000) N'abc', N'def', N'ghi', N'jkl', N'mno', N'pqr'
 
-- and to show when all values will change, a different variable assignment would take place:
 
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';

Resultaten

Na het uitvoeren van deze tests won de "blinde update" in elk afzonderlijk scenario. Nu denk je, wat is een paar honderd milliseconden? Extrapoleren. Als je veel updates in je systeem uitvoert, kan dit echt zijn tol gaan eisen.

Gedetailleerde resultaten in Plan Explorer:Elke wijziging | 2 waarden vrijgesteld | 4 waarden vrijgesteld | Alle waarden vrijgesteld | Alles verandert

Op basis van feedback van Roji besloot ik dit ook met een paar indexen te testen:

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);

De looptijden werden aanzienlijk verhoogd met deze indexen:

Gedetailleerde resultaten in Plan Explorer:Elke wijziging | 2 waarden vrijgesteld | 4 waarden vrijgesteld | Alle waarden vrijgesteld | Alles verandert

Conclusie

Uit deze test lijkt het mij dat het meestal niet de moeite waard is om te controleren of een waarde moet worden bijgewerkt. Als uw UPDATE-instructie van invloed is op meerdere kolommen, is het bijna altijd goedkoper voor u om alle kolommen te scannen waar een waarde mogelijk is gewijzigd, in plaats van elke kolom afzonderlijk te controleren. In een toekomstige post zal ik onderzoeken of dit scenario parallel loopt voor LOB-kolommen.


  1. MySQL OP DUPLICATE KEY - laatste invoeg-ID?

  2. Aan de slag met Oracle SQL Developer op MacOSX

  3. Beperkingen van SQL Server Express

  4. ActiveRecord::AdapterNotSpecified databaseconfiguratie specificeert geen adapter