Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Aktualisieren, falls anders/geändert

Während der Abfragekompilierung und -ausführung nimmt sich SQL Server nicht die Zeit herauszufinden, ob eine UPDATE-Anweisung tatsächlich Werte ändert oder nicht. Es führt nur die Schreibvorgänge wie erwartet aus, auch wenn sie unnötig sind.

In dem Szenario wie

update table1 set col1 = 'hello'

Sie denken vielleicht, dass SQL nichts tut, aber das wird es – es führt alle notwendigen Schreibvorgänge aus, als ob Sie den Wert tatsächlich geändert hätten. Dies tritt sowohl für die physische Tabelle (oder den gruppierten Index) als auch für alle nicht gruppierten Indizes auf, die für diese Spalte definiert sind. Dies führt zu Schreibvorgängen in die physischen Tabellen/Indizes, zur Neuberechnung von Indizes und zu Schreibvorgängen im Transaktionsprotokoll. Bei der Arbeit mit großen Datensätzen bietet es enorme Leistungsvorteile, nur die Zeilen zu aktualisieren, die eine Änderung erfahren werden.

Wenn wir den Overhead dieser nicht notwendigen Schreibvorgänge vermeiden möchten, müssen wir einen Weg finden, um zu überprüfen, ob eine Aktualisierung erforderlich ist. Eine Möglichkeit, um zu prüfen, ob eine Aktualisierung erforderlich ist, besteht darin, etwas hinzuzufügen wie „where col <> 'hello'.

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

Dies würde jedoch in einigen Fällen nicht gut funktionieren, beispielsweise wenn Sie mehrere Spalten in einer Tabelle mit vielen Zeilen aktualisieren und nur eine kleine Teilmenge dieser Zeilen tatsächlich ihre Werte ändern würde. Dies liegt an der Notwendigkeit, nach all diesen Spalten zu filtern, und Prädikate ohne Gleichheit sind im Allgemeinen nicht in der Lage, Indexsuchen und den Overhead von Tabellen- und Indexschreibvorgängen und Transaktionsprotokolleinträgen, wie oben erwähnt, zu verwenden.

Aber es gibt eine viel bessere Alternative, die eine Kombination einer EXISTS-Klausel mit einer EXCEPT-Klausel verwendet. Die Idee besteht darin, die Werte in der Zielzeile mit den Werten in der übereinstimmenden Quellzeile zu vergleichen, um festzustellen, ob tatsächlich eine Aktualisierung erforderlich ist. Sehen Sie sich die modifizierte Abfrage unten an und untersuchen Sie den zusätzlichen Abfragefilter, der mit EXISTS beginnt. Beachten Sie, dass die SELECT-Anweisungen innerhalb der EXISTS-Klausel keine FROM-Klausel haben. Dieser Teil ist besonders wichtig, da dies nur einen zusätzlichen konstanten Scan und eine Filteroperation im Abfrageplan hinzufügt (die Kosten für beide sind trivial). Am Ende erhalten Sie also eine sehr einfache Methode, um festzustellen, ob ein UPDATE überhaupt erforderlich ist, und unnötigen Schreibaufwand zu vermeiden.

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

Dies sieht übermäßig kompliziert aus, wenn Sie in einer einfachen WHERE-Klausel für das einfache Szenario in der ursprünglichen Frage nach Aktualisierungen suchen, wenn Sie einen Wert für alle Zeilen in einer Tabelle mit einem Literalwert aktualisieren. Diese Technik funktioniert jedoch sehr gut, wenn Sie mehrere Spalten in einer Tabelle aktualisieren und die Quelle Ihrer Aktualisierung eine andere Abfrage ist und Sie Schreibvorgänge und Transaktionsprotokolleinträge minimieren möchten. Es funktioniert auch besser, als jedes Feld mit <>.

zu testen

Ein vollständigeres Beispiel könnte sein

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
    )