Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Aggiorna se diverso/modificato

Durante la compilazione e l'esecuzione delle query, SQL Server non ha bisogno di tempo per capire se un'istruzione UPDATE modificherà effettivamente i valori o meno. Esegue semplicemente le scritture come previsto, anche se non necessario.

Nello scenario come

update table1 set col1 = 'hello'

potresti pensare che SQL non farà nulla, ma lo farà:eseguirà tutte le scritture necessarie come se avessi effettivamente modificato il valore. Ciò si verifica sia per la tabella fisica (o indice cluster) sia per tutti gli indici non cluster definiti su quella colonna. Ciò provoca scritture nelle tabelle/indici fisici, il ricalcolo degli indici e le scritture del registro delle transazioni. Quando si lavora con set di dati di grandi dimensioni, si ottengono enormi vantaggi in termini di prestazioni se si aggiornano solo le righe che riceveranno una modifica.

Se vogliamo evitare il sovraccarico di queste scritture quando non necessarie, dobbiamo escogitare un modo per verificare la necessità di essere aggiornati. Un modo per verificare la necessità di aggiornare sarebbe aggiungere qualcosa come "where col <> 'hello'.

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

Ma questo non funzionerebbe bene in alcuni casi, ad esempio se si aggiornano più colonne in una tabella con molte righe e solo un piccolo sottoinsieme di quelle righe verrebbe effettivamente modificato i loro valori. Ciò è dovuto alla necessità di filtrare su tutte quelle colonne e i predicati di non uguaglianza generalmente non sono in grado di utilizzare le ricerche di indice e il sovraccarico delle scritture di tabelle e indici e delle voci del registro delle transazioni come menzionato sopra.

Ma c'è un'alternativa molto migliore usando una combinazione di una clausola EXISTS con una clausola EXCEPT. L'idea è confrontare i valori nella riga di destinazione con i valori nella riga di origine corrispondente per determinare se è effettivamente necessario un aggiornamento. Guarda la query modificata di seguito ed esamina il filtro di query aggiuntivo che inizia con EXISTS. Nota come all'interno della clausola EXISTS le istruzioni SELECT non hanno clausola FROM. Quella parte è particolarmente importante perché aggiunge solo un'ulteriore scansione costante e un'operazione di filtro nel piano di query (il costo di entrambi è banale). Quindi quello che si ottiene è un metodo molto leggero per determinare se è necessario un AGGIORNAMENTO in primo luogo, evitando inutili sovraccarico di scrittura.

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

Sembra eccessivamente complicato rispetto al controllo degli aggiornamenti in una semplice clausola WHERE per il semplice scenario nella domanda originale quando si aggiorna un valore per tutte le righe in una tabella con un valore letterale. Tuttavia, questa tecnica funziona molto bene se si aggiornano più colonne in una tabella e l'origine dell'aggiornamento è un'altra query e si desidera ridurre al minimo le scritture e le voci dei registri delle transazioni. Ha anche prestazioni migliori rispetto a testare ogni campo con <>.

Un esempio più completo potrebbe essere

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
    )