T-SQL & quando gli update non aggiornano nessuna riga
Carissimi lettori,
Oggi vorrei aprire una parentesi nella nostra carrellata di articoli che trattano delle novità di SQL Server 2019 per parlarvi di una domanda interessante che mi è stata posta qualche giorno fa.
La domanda era più o meno questa:
Cosa succede quando eseguo un UPDATE che non va a aggiornare nessun dato perchè il suo valore è già quello che io voglio impostare?
Un update che non aggiorna nessuna riga
Per fare un esempio prendiamo la tabella creata nello scorso articolo.
Supponiamo quindi che la colonna NOME abbia già il valore "LUCA"
Il nostro primo UPDATE sarebbe questo:
UPDATE ELENCOVENDITE
SET
CLIENTE = 'HHH'
WHERE
ID = 8
La domanda continuava chiedendo se, ai fini delle prestazioni, in questo caso non fosse meglio aggiungere una condizione nella clausola WHERE:
UPDATE ELENCOVENDITE
SET
CLIENTE = 'HHH'
WHERE
ID = 8 AND CLIENTE<> 'HHH'
Siete pronti per vedere cosa accade esattamente durante un update?
Seguitemi!
Analizzeremo prima il log delle transazioni e poi i lock generati nei vari casi.
Anzi, per trattare l'argomento in modo completo partiremo dal "caso base" in cui la prima query effettivamente va ad aggiornare la riga (quindi il campo CLIENTE <> 'HHH')
Caso base: il nostro update aggiorna una riga.
Osservando il log delle transazioni vediamo che sono presenti 9 righe di cui 4 sono rappresentate dal nostro update
Adesso eseguiamo il nostro UPDATE tracciandolo tramite il profiler estraendo gli eventi relativi ai lock.
Osserviamo che vengono acquisiti i seguenti lock :
1 lock a livello di OBJECT (5) di tipo IX (IX=intent exclusive)
5 lock a livello di PAGE(6) di cui 4 IX ed 1 IU (IU=intent update)
5 lock a livello di KEY (7) di cui 3 IX, 1 U e 1 RANGE I-N (U=update lock) (RANGE-I=insert range)
Caso in cui non viene aggiornato alcun valore
Ora confrontiamo i dati appena ottenuti con il caso in cui
il nostro UPDATE non aggiorni nessuna riga: metto nel campo cliente un
valore uguale a quello già presente.
La stringa SQL da eseguire è la stessa:
UPDATE ELENCOVENDITE
SET
CLIENTE = 'HHH'
WHERE
ID = 8
Vediamo il log delle transazioni:
Il numero delle righe passa da 9 a 5 e se ci fate caso mancano proprio le 4 righe che derivano dal nostro update.
E questo cosa vuol dire?
Lo scrivo in blu proprio per evidenziare cosa occorre ricordarsi dall' articolo di oggi:
SQL Server è in grado di accorgersi che il valore già contenuto nella tabella è uguale a quello che gli vorremmo mettere! In questo caso non sovrascrive il valore in destinazione ma evita di scriverlo.
Indaghiamo però cosa succede a livello dei lock che vengono posti sui vari oggetti.
Questo aspetto è molto importante quando i vari statements T-SQL vengono eseguiti da più clients contemporaneamente.
Calano il numero di lock da 11 a 5 che comunque rimangono presenti.
Come accade nell'esempio precedente viene comunque posto un lock a livello di oggetto di tipo IX.
Sono poi presenti 2 blocchi a livello di pagina,uno IU ed uno IX
Sono infine presenti altri 2 blocchi a livello di key, uno di tipo U ed uno di tipo X.
1 lock a livello di OBJECT (5) di tipo IX (IX=intent exclusive)
2 lock a livello di PAGE(6) di cui 1 IX ed 1 IU (IU=intent update)
2 lock a livello di KEY (7) di cui 1 IX, 1 U (U=update lock)
Ultimo caso
Con l'ultimo caso vediamo cosa accade nella Query:
UPDATE ELENCOVENDITE
SET
CLIENTE = 'HHH'
WHERE
ID = 8 AND CLIENTE<> 'HHH'
Nel log delle transazioni appaiono solamente le tre righe evidenziate:
Mancano infatti le due righe di LOOP_BEGIN_XACT e LOOP_COMMIT_XACT
A livello di lock ne abbiamo 3 al posto di 5.
Che nel dettaglio sono:
1 lock a livello di OBJECT (5) di tipo IX (IX=intent exclusive)
1 lock a livello di PAGE(6) di tipo IU (IU=intent update)
1 lock a livello di KEY (7) di tipo U (U=update lock)
Bene, fino a questo punto abbiamo capito che la soluzione che prevende l'aggiunta nella clausola WHERE della condizione aggiuntiva sembra essere (in modo difficilmente quantificabile!) migliore.
UPDATE ELENCOVENDITE
SET
CLIENTE = 'HHH'
WHERE
ID = 8 AND CLIENTE<> 'HHH'
Però c'è un però!
Vediamo di confrontare le due Query tramite il piano di esecuzione.
Innanzitutto attenzione: aggiungere una (ulteriore) condizione ad una WHERE porta anche ad una elaborazione in qualche modo più pesante.
Se infatti osserviamo il piano di esecuzione sotto vediamo che la condizione aggiuntiva si traduce in un operatore di calcolo scalare.
E sorprendentemente alla prova pratica il tempo di esecuzione delle due Query è simile!
Traiamo quindi le conclusioni
La risposta all'interessante quesito è quindi purtroppo: dipende!
Nel caso (semplice) che abbiamo utilizzato per fare la nostra analisi le differenze sono minime tanto che è difficile quantificarle.
In un caso più reale e più complesso dove sono in gioco, più colonne e dopo la where sono indicate molte condizione la strada migliore è quella di verificare direttamente la Query che stiamo utilizzantp. Solo così si capirà qual'è soluzione migliore!
Luca Biondi @ SQLServerPerformance blog!
Next post: SQL Server e gli Extended Event
Previous post: SQL Server 2019 e la funzione Approx_Count_Distinct
Comments
Post a Comment