2012-07-06 10 views
5

Sto usando colonne rowversion per gestire la concorrenza ottimistica e voglio ottenere il nuovo valore di rowversion quando ho fatto un aggiornamento in modo che il mio livello dati abbia l'ultimo valore e può eseguire un altro aggiornamento ottenendo un'eccezione di concorrenza (a meno che il record non sia stato aggiornato da qualcun altro).Ottenere l'ultimo valore rowversion/timestamp nell'istruzione update - Sql Server

Stavo solo facendo un get nel livello dati dopo aver fatto un aggiornamento, ma questo non era molto efficiente o perfettamente affidabile.

Per la tabella seguente:

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

ho provato:

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 

ma non era del tutto felice a causa della possibilità di non ottenere il valore rowverion da aggiornamento di qualcun altro. Poi mi sono imbattuto la dichiarazione OUTPUT nella documentazione rowversion (http://msdn.microsoft.com/en-us/library/ms182776.aspx/http://msdn.microsoft.com/en-us/library/ms177564.aspx) e provato questo:

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 

questo funziona bene. Nei miei test di base (solo eseguendo 10000 chiamate e cronometraggio) l'opzione OUTPUT impiega circa il 40% in più ma ancora meno di mezzo millisecondo. Nessuno dei due ha avuto tempo misurabile con SET STATISTICS TIME ON.

La mia domanda è, qualcuno sa di un modo migliore/più semplice per farlo?

Avevo sperato in una funzione che potrei usare simile a SCOPE_IDENTITY() per le colonne Identity ma non riesco a trovare nulla di simile. Qualcuno sa se mi manca qualcosa?

Grazie in anticipo.

+0

OUTPUT è la strada da percorrere per recuperare qualsiasi valore generato su INSERT (e che comprende IDENTITÀ se mi chiedete ...) –

+0

OK, grazie per questo. Qualche ragione particolare per cui preferisci OUTPUT rispetto a SCOPE_IDENTITY? –

+2

Funziona con inserti multiriga –

risposta

0

Da MSDN:

@@ DBTS restituisce l'ultimo utilizzato il valore timestamp del database corrente. Un nuovo valore di data/ora viene generato quando una riga con una colonna timestamp viene inserita o aggiornata.

+2

Grazie, non lo sapevo. A meno che non mi sbagli. non funzionerebbe in modo affidabile in questo scenario perché è ampio come database (simile a @@ identity) piuttosto che ambito alla procedura (simile a SCOPE_IDENTITY()). –

+0

Hai ragione, questo è un aspetto negativo. Immagino che OUTPUT sia l'unica via da percorrere. –

0

Il tuo problema è che tra l'aggiornamento e selezionare qualcun altro aggiorna le righe. Quindi quando selezioni dopo la tua dichiarazione di aggiornamento non ottieni gli stessi valori. Questo è un problema di lettura non ripetibile. Dovresti usare il comando di chiusura (UPDLOCK) con la dichiarazione di aggiornamento. Questo blocco consentirà ai lettori (che sono blocchi condivisi) ma bloccherà gli scrittori. Quindi otterrai le stesse righe dopo l'aggiornamento. Così la stored procedure deve essere -

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 
Problemi correlati