Wenn ich über eine UPDATE-Anweisung verfüge, die tatsächlich keine Daten ändert (da sich die Daten bereits im aktualisierten Zustand befinden), hat es einen Leistungsvorteil, die where-Klausel zu überprüfen, um die Aktualisierung zu verhindern?
Dies kann durchaus sein, da es aufgrund von UPDATE 1 einen geringfügigen Leistungsunterschied gibt :
- keine Zeilen aktualisieren (daher nichts zum Schreiben auf die Festplatte, nicht einmal minimale Protokollaktivität) und
- Entfernen weniger restriktiver Sperren als für das eigentliche Update erforderlich (daher besser für die gleichzeitige Verwendung) ( siehe Update-Abschnitt zum Ende )
Wie groß der Unterschied ist, müsste von Ihnen auf Ihrem System anhand Ihres Schemas, Ihrer Daten und der Systemlast gemessen werden. Es gibt verschiedene Faktoren, die bestimmen, wie stark sich ein nicht aktualisiertes UPDATE auswirkt:
- Der Umfang der Konflikte in der Tabelle, die aktualisiert werden
- Die Anzahl der zu aktualisierenden Zeilen
- wenn die zu aktualisierende Tabelle UPDATE-Trigger enthält (wie von Mark in einem Kommentar zur Frage angegeben). Wenn Sie ausführen
UPDATE TableName SET Field1 = Field1
, wird ein Update-Trigger ausgelöst , der angibt, dass das Feld aktualisiert wurde (wenn Sie dies mit den Funktionen UPDATE () oder COLUMNS_UPDATED überprüfen ) und dass das Feld in beiden INSERTED
und den DELETED
Tabellen den gleichen Wert hat.
Der folgende zusammenfassende Abschnitt ist auch in Paul Whites Artikel " Die Auswirkungen nicht aktualisierender Updates" zu finden (wie von @spaghettidba in einem Kommentar zu seiner Antwort vermerkt):
SQL Server enthält eine Reihe von Optimierungen, um unnötiges Protokollieren oder Leeren von Seiten bei der Verarbeitung eines UPDATE-Vorgangs zu vermeiden, der zu keiner Änderung der persistenten Datenbank führt.
- Durch das Nicht-Aktualisieren von Aktualisierungen einer gruppierten Tabelle werden im Allgemeinen zusätzliche Protokollierungs- und Seitenlöschvorgänge vermieden, es sei denn, eine Spalte, die (einen Teil) des Cluster-Schlüssels bildet, ist vom Aktualisierungsvorgang betroffen.
- Wenn ein Teil des Cluster-Schlüssels auf denselben Wert aktualisiert wird, wird der Vorgang protokolliert, als hätten sich Daten geändert, und die betroffenen Seiten werden im Pufferpool als verschmutzt markiert. Dies ist eine Folge der Konvertierung von UPDATE in eine Lösch-Einfüge-Operation.
- Heap-Tabellen verhalten sich genauso wie gruppierte Tabellen, außer dass sie keinen Cluster-Schlüssel haben, um zusätzliche Protokollierung oder Seitenlöschung zu verursachen. Dies bleibt auch dann der Fall, wenn auf dem Heap ein nicht gruppierter Primärschlüssel vorhanden ist. Aktualisierungen auf einem Heap, die nicht aktualisiert werden, vermeiden daher im Allgemeinen das zusätzliche Protokollieren und Löschen (siehe jedoch unten).
- Sowohl Heaps als auch gruppierte Tabellen werden für jede Zeile, in der eine LOB-Spalte mit mehr als 8000 Datenbytes mit einer anderen Syntax als 'SET Spaltenname = Spaltenname' auf denselben Wert aktualisiert wird, zusätzlich protokolliert und geleert.
- Das Aktivieren einer der beiden Isolationsstufen für die Zeilenversionierung in einer Datenbank führt immer zu einer zusätzlichen Protokollierung und Löschung. Dies geschieht unabhängig von der für die Aktualisierungstransaktion geltenden Isolationsstufe.
Bitte beachten Sie (insbesondere, wenn Sie nicht dem Link folgen, um den vollständigen Artikel von Paul anzuzeigen) die folgenden beiden Punkte:
Aktualisierungen, die nicht aktualisiert werden, weisen noch einige Protokollaktivitäten auf, die darauf hinweisen, dass eine Transaktion beginnt und endet. Es findet nur keine Datenänderung statt (was immer noch eine gute Einsparung ist).
Wie oben erwähnt, müssen Sie auf Ihrem System testen. Verwenden Sie dieselben Suchanfragen, die Paul verwendet, und prüfen Sie, ob Sie dieselben Ergebnisse erhalten. Ich sehe auf meinem System etwas andere Ergebnisse als in dem Artikel gezeigt. Es müssen noch keine schmutzigen Seiten geschrieben werden, sondern etwas mehr Protokollaktivität.
... Die Zeilenanzahl muss die unveränderte Zeile enthalten, damit ich weiß, ob eine Einfügung erfolgen soll, wenn die ID nicht vorhanden ist. ... ist es möglich, die Anzahl der Zeilen zu ermitteln, die ich irgendwie brauche?
Vereinfacht gesagt, wenn Sie sich nur mit einer einzelnen Zeile befassen, können Sie Folgendes tun:
UPDATE MyTable
SET Value = 2
WHERE ID = 2
AND Value <> 2;
IF (@@ROWCOUNT = 0)
BEGIN
IF (NOT EXISTS(
SELECT *
FROM MyTable
WHERE ID = 2 -- or Value = 2 depending on the scenario
)
)
BEGIN
INSERT INTO MyTable (ID, Value) -- or leave out ID if it is an IDENTITY
VALUES (2, 2);
END;
END;
Bei mehreren Zeilen können Sie mithilfe der OUTPUT
Klausel die Informationen abrufen, die für diese Entscheidung erforderlich sind . Indem Sie genau erfassen, welche Zeilen aktualisiert wurden, können Sie die Elemente eingrenzen, um den Unterschied zwischen dem Nicht-Aktualisieren von nicht vorhandenen Zeilen und dem Nicht-Aktualisieren von vorhandenen Zeilen, die jedoch nicht aktualisiert werden müssen, zu ermitteln.
Ich zeige die grundlegende Implementierung in der folgenden Antwort:
Wie vermeide ich die Verwendung der Zusammenführungsabfrage, wenn mehrere Daten mit dem xml-Parameter aktualisiert werden?
Die in dieser Antwort gezeigte Methode filtert keine vorhandenen Zeilen heraus, die noch nicht aktualisiert werden müssen. Dieser Teil könnte hinzugefügt werden, aber Sie müssen zunächst genau angeben, woher Sie Ihr Dataset beziehen, in dem Sie zusammenführen MyTable
. Kommen sie von einem temporären Tisch? Ein tabellenwertiger Parameter (TVP)?
UPDATE 1:
Ich konnte endlich einige Tests durchführen und hier ist, was ich in Bezug auf Transaktionsprotokoll und Sperren gefunden habe. Zuerst das Schema für die Tabelle:
CREATE TABLE [dbo].[Test]
(
[ID] [int] NOT NULL CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED,
[StringField] [varchar](500) NULL
);
Als nächstes aktualisiert der Test das Feld auf den Wert, den es bereits hat:
UPDATE rt
SET rt.StringField = '04CF508B-B78E-4264-B9EE-E87DC4AD237A'
FROM dbo.Test rt
WHERE rt.ID = 4082117
Ergebnisse:
-- Transaction Log (2 entries):
Operation
----------------------------
LOP_BEGIN_XACT
LOP_COMMIT_XACT
-- SQL Profiler (3 Lock:Acquired events):
Mode Type
--------------------------------------
8 - IX 5 - OBJECT
8 - IX 6 - PAGE
5 - X 7 - KEY
Zum Schluss der Test, der das Update herausfiltert, weil sich der Wert nicht ändert:
UPDATE rt
SET rt.StringField = '04CF508B-B78E-4264-B9EE-E87DC4AD237A'
FROM dbo.Test rt
WHERE rt.ID = 4082117
AND rt.StringField <> '04CF508B-B78E-4264-B9EE-E87DC4AD237A';
Ergebnisse:
-- Transaction Log (0 entries):
Operation
----------------------------
-- SQL Profiler (3 Lock:Acquired events):
Mode Type
--------------------------------------
8 - IX 5 - OBJECT
7 - IU 6 - PAGE
4 - U 7 - KEY
Wie Sie sehen, wird beim Herausfiltern der Zeile nichts in das Transaktionsprotokoll geschrieben, im Gegensatz zu den beiden Einträgen, die den Anfang und das Ende der Transaktion markieren. Und obwohl es stimmt, dass diese beiden Einträge fast nichts sind, sind sie immer noch etwas.
Außerdem ist das Sperren der Ressourcen PAGE und KEY weniger restriktiv, wenn die nicht geänderten Zeilen herausgefiltert werden. Wenn keine anderen Prozesse mit dieser Tabelle interagieren, ist dies wahrscheinlich kein Problem (aber wie wahrscheinlich ist das wirklich?). Denken Sie daran, dass diese Tests, die in einem der verlinkten Blogs (und sogar in meinen Tests) gezeigt werden, implizit davon ausgehen, dass keine Konflikte auf dem Tisch liegen, da sie niemals Teil der Tests sind. Zu sagen, dass nicht aktualisierte Updates so leicht sind, dass sich das Filtern nicht lohnt, muss mit einem Salzkorn genommen werden, da die Tests mehr oder weniger im luftleeren Raum durchgeführt wurden. In der Produktion ist diese Tabelle jedoch höchstwahrscheinlich nicht isoliert. Natürlich könnte es durchaus sein, dass das bisschen Protokollieren und restriktivere Sperren nicht zu einer geringeren Effizienz führen. Die zuverlässigste Informationsquelle zur Beantwortung dieser Frage? SQL Server. Speziell:Ihren SQL Server. Es wird Ihnen zeigen, welche Methode für Ihr System besser ist :-).
UPDATE 2:
Wenn die Vorgänge, bei denen der neue Wert mit dem aktuellen Wert übereinstimmt (dh keine Aktualisierung), die Vorgänge übersteigen, bei denen der neue Wert abweicht und die Aktualisierung erforderlich ist, kann sich das folgende Muster als noch besser erweisen, insbesondere wenn Auf dem Tisch liegt viel Streit. Die Idee ist, zuerst einen einfachen SELECT
Schritt zu machen, um den aktuellen Wert zu erhalten. Wenn Sie keinen Wert erhalten, haben Sie Ihre Antwort bezüglich der INSERT
. Wenn Sie einen Wert haben, können Sie einen einfachen Wert nur dann IF
ausgeben, wenn er benötigt wird.UPDATE
DECLARE @CurrentValue VARCHAR(500) = NULL,
@NewValue VARCHAR(500) = '04CF508B-B78E-4264-B9EE-E87DC4AD237A',
@ID INT = 4082117;
SELECT @CurrentValue = rt.StringField
FROM dbo.Test rt
WHERE rt.ID = @ID;
IF (@CurrentValue IS NULL) -- if NULL is valid, use @@ROWCOUNT = 0
BEGIN
-- row does not exist
INSERT INTO dbo.Test (ID, StringField)
VALUES (@ID, @NewValue);
END;
ELSE
BEGIN
-- row exists, so check value to see if it is different
IF (@CurrentValue <> @NewValue)
BEGIN
-- value is different, so do the update
UPDATE rt
SET rt.StringField = @NewValue
FROM dbo.Test rt
WHERE rt.ID = @ID;
END;
END;
Ergebnisse:
-- Transaction Log (0 entries):
Operation
----------------------------
-- SQL Profiler (2 Lock:Acquired events):
Mode Type
--------------------------------------
6 - IS 5 - OBJECT
6 - IS 6 - PAGE
Es werden also nur 2 statt 3 Sperren erworben, und beide Sperren sind Absichtsfreigaben, nicht Absichtsexklusiv oder Absichtsaktualisierung ( Sperrenkompatibilität ). Berücksichtigt man, dass jede erworbene Sperre auch freigegeben wird, handelt es sich bei jeder Sperre tatsächlich um 2 Vorgänge. Bei dieser neuen Methode handelt es sich also um insgesamt 4 Vorgänge anstelle der 6 Vorgänge der ursprünglich vorgeschlagenen Methode. Berücksichtigt man, dass dieser Vorgang einmal alle 15 ms ausgeführt wird (ungefähr, wie vom OP angegeben), dh ungefähr 66 Mal pro Sekunde. Der ursprüngliche Vorschlag umfasst also 396 Sperr- / Entsperrvorgänge pro Sekunde, während diese neue Methode nur 264 Sperr- / Entsperrvorgänge pro Sekunde für noch leichtere Sperren umfasst. Dies ist keine Garantie für großartige Leistung, aber auf jeden Fall einen Test wert :-).