2016-05-13 5 views
0

Ich habe einige Synchronisationsprozesse, die ein Flag "LastUpdate" verwenden, um alle Datensätze zu aktualisieren, die seit dem letzten Synchronisierungsversuch geändert wurden.SQL-Merge, Tabellenwertparameter und GetDate()

Vor einiger Zeit habe ich den Code aktualisiert, um Tabellenwerte zu verwenden, anstatt eine Zeile auf einmal zu synchronisieren (hinzufügen/aktualisieren). Dies ist 10 mal oder mehr schneller.

Allerdings bin ich jetzt auf eine Race Condition gestoßen, die manchmal zu Updates führt. Ich schnell einig SQL-Skript rauschte bis zu meiner Situation/Theorie zu testen (jede große Tabelle mit IDs arbeiten):

/*CREATE TYPE IntTable AS TABLE(
[RequestID] [int] NOT NULL 
) 
GO 

CREATE TABLE MergeTest(
[ID] [int] IDENTITY(1,1) NOT NULL, 
[RequestID] [int] NOT NULL, 
[PreDate] [datetime] NOT NULL, 
[MergeDate] [datetime] NOT NULL 
GO 
*/ 

DECLARE @requestIDs As IntTable 

INSERT INTO @requestIDs 
SELECT RequestID FROM Request 

DECLARE @preDate As DateTime = Getdate() 

MERGE INTO MergeTest USING @requestIDs SRC 
ON MergeTest.RequestID = SRC.RequestID 
WHEN MATCHED THEN 
    UPDATE SET PreDate = @preDate, MergeDate = GetDate() 
WHEN NOT MATCHED THEN 
    INSERT (RequestID, PreDate, MergeDate) 
    VALUES (SRC.RequestID, @preDate, GetDate()); 

SELECT TOP 100 * FROM MergeTest 

Beispiel Ergebnis

ID RequestID PreDate     MergeDate 
1 169880  2016-05-13 13:57:54.643 2016-05-13 13:57:54.643 

So können Sie sehen, dass die MergeDate (GetDate ()) ist ab wann die Zusammenführung beginnt, nicht wenn sie endet.

Die Race-Bedingung kann das sein:

Check what has been updated since 14:59 
Start a merge at 15:00 
Check what has been updated since 15:00 
Merge completes, but with a LastUpdate of 15:00 
Check what has been updated since 15:01 

Alle Datensätze aus der Zusammenführung würde übersprungen. In der Realität tritt diese Race Condition sehr selten auf, weil wir Millisekunden statt Minuten sprechen, aber es passiert.

Die Frage ist ... ohne ein zweites Skript zu laufen, das LastUpdate mit einem Post-Merge-Datum neu zu aktualisieren, gibt es eine Möglichkeit, die Merge-Anweisung das Datum zu verwenden, dass es den Auftrag beendet hat, anstatt wann begann es?

Antwort

0

Anstatt das Einstellen LastUpdate (oder MergeDate, in Ihrem Beispielcode), zu getdate(), etwas zu tun, wie folgt aus:

declare @MergeDate DateTime = getdate() 

<merge code...> 
set MergeDate = @MergeDate 
<...> 

Auf diese Weise der Zeitstempel gleich, wenn die Zusammenführung begann, nicht beendet. Sie können dann einige Zeilen mehr als einmal verarbeiten, aber das ist ein Fehler der Aufnahme statt des Ausschlusses und sollte keinen Einfluss auf die Ergebnisse haben.

0

Anstatt zu versuchen, SQL zu zwingen, die Endzeit in der Zusammenführung zu verwenden (was ich nicht sehen kann), warum nicht die Startzeit jeder Zusammenführung in einer Tabelle speichern (lasst uns dLastRunDate anrufen).

Wenn Sie die nächste Zusammenführung starten, verwenden Sie nicht getdate() - nehmen Sie die dLastRunDate aus der neuen Tabelle und verwenden Sie diese, um nach neuen Datensätzen zu suchen.

Am Ende des Jobs aktualisieren Sie dLastRunDate auf seinen neuen Wert.

Wir verwenden diesen Ansatz in unserem Lager ETLS. Jeder Schritt hat einen Eintrag in der Tabelle. Jedes Mal, wenn der Job einen Schritt startet, nimmt er seine eigene dLastRunDate auf und verwendet diese, um nach aktualisierten Datensätzen zu suchen. Wenn der Schritt abgeschlossen ist, aktualisiert er dLastRunDate mit seiner Startzeit.