2012-07-06 9 views
5

Ich verwende Rowversion Spalten für die Behandlung optimistischer Nebenläufigkeit und möchte den neuen Zeilversion Wert zurück, wenn ich ein Update gemacht habe, so dass meine Datenschicht den neuesten Wert hat und kann ein weiteres Update mit dem Erhalt einer Parallelitätsausnahme durchführen (es sei denn, der Datensatz wurde von jemand anderem aktualisiert).Aktuellen Zeilversion/Timestamp-Wert in Update-Anweisung erhalten - Sql Server

Ich war gerade dabei, in die Datenschicht zu gelangen, nachdem ich ein Update gemacht hatte, aber das war nicht sehr effizient oder absolut zuverlässig.

Für die folgende Tabelle:

CREATE TABLE PurchaseType 
(
    PurchaseTypeCode nvarchar(20) NOT NULL PRIMARY KEY CLUSTERED (PurchaseTypeCode), 
    Name nvarchar(50) NOT NULL, 
    TS rowversion NOT NULL 
) 

Ich habe versucht:

CREATE PROCEDURE PurchaseType_UpdateWithGet 
@PurchaseTypeCode nvarchar(20), 
@Name nvarchar(50), 
@TS rowversion OUTPUT  
AS 

UPDATE PurchaseType 
SET Name = @Name 
WHERE PurchaseTypeCode = @PurchaseTypeCode 
AND TS = @TS 

SELECT @TS = TS FROM PurchaseType WHERE PurchaseTypeCode = @PurchaseTypeCode 
GO 

war aber wegen der Möglichkeit, nicht immer den rowverion Wert von jemandem anderen Update nicht ganz zufrieden. Dann kam ich auf die OUTPUT-Anweisung in rowversion Dokumentation (http://msdn.microsoft.com/en-us/library/ms182776.aspx/http://msdn.microsoft.com/en-us/library/ms177564.aspx) und versucht, dies:

CREATE PROCEDURE PurchaseType_UpdateWithOutput 
@PurchaseTypeCode nvarchar(20), 
@Name nvarchar(50), 
@TS rowversion OUTPUT  
AS 

DECLARE @Output TABLE (TS BINARY(8)) 

UPDATE PurchaseType 
SET Name = @Name 
    OUTPUT inserted.TS into @Output 
WHERE PurchaseTypeCode = @PurchaseTypeCode 
    AND TS = @TS 

SELECT TOP 1 @TS = TS FROM @Output 
GO 

Das funktioniert gut. In meinen grundlegenden Tests (10000 Aufrufe und Timing) dauert die OUTPUT-Option etwa 40% länger, aber immer noch weniger als eine halbe Millisekunde. Bei SET STATISTICS TIME ON wurde keine messbare Zeit benötigt.

Meine Frage ist, kennt jemand eine bessere/einfachere Möglichkeit, dies zu tun?

Ich hatte auf eine Funktion gehofft, die ich ähnlich wie SCOPE_IDENTITY() für Identitätsspalten verwenden konnte, aber nichts dergleichen finden kann. Wer weiß, ob ich etwas verpasse?

Vielen Dank im Voraus.

+0

Ausgang ist der Weg zu gehen jeden generierten Wert auf INSERT abgerufen werden (und das schließt IDENTITY wenn Sie mich fragen ...) –

+0

OK, danke dafür. Gibt es einen besonderen Grund, warum Sie OUTPUT gegenüber SCOPE_IDENTITY bevorzugen? –

+2

Es funktioniert mit mehrreihigen Einfügungen –

Antwort

0

Von MSDN:

@@ DBTS gibt den zuletzt verwendeten Zeitstempel-Wert der aktuellen Datenbank. Ein neuer Timestamp-Wert wird generiert, wenn eine Zeile mit einer Timestamp-Spalte eingefügt oder aktualisiert wird.

+2

Danke, das wusste ich nicht. Es sei denn, ich täusche mich. Es würde in diesem Szenario nicht zuverlässig funktionieren, da es datenbankweit (ähnlich wie @@ identity) statt auf die Prozedur beschränkt ist (ähnlich SCOPE_IDENTITY()). –

+0

Sie haben Recht, das ist ein Nachteil. Ich denke, OUTPUT ist der einzige Weg, den es zu verlassen gilt. –

0

Ihr Problem ist, dass zwischen Update und wählen Sie jemand anderes aktualisiert die Zeilen. Wenn Sie also nach Ihrer Update-Anweisung auswählen, erhalten Sie nicht die gleichen Werte. Dies ist ein Problem des nicht wiederholbaren Lesevorgangs. Sie sollten den Sperrhinweis (UPDLOCK) mit update-Anweisung verwenden. Diese Sperre ermöglicht Lesern (dh freigegebene Sperren), blockiert jedoch Writer. So erhalten Sie nach dem Update die gleichen Zeilen. So sollten Sie Ihre gespeicherte Prozedur sein -

CREATE PROCEDURE PurchaseType_UpdateWithGet 
@PurchaseTypeCode nvarchar(20), 
@Name nvarchar(50), 
@TS rowversion OUTPUT  
AS 

UPDATE PurchaseType with (updlock) 
SET Name = @Name 
WHERE PurchaseTypeCode = @PurchaseTypeCode 
AND TS = @TS 

SELECT @TS = TS FROM PurchaseType WHERE PurchaseTypeCode = @PurchaseTypeCode 
GO 
Verwandte Themen