Emulieren Sie eine TSQL-Sequenz über eine gespeicherte Prozedur


17

Ich muss eine gespeicherte Prozedur erstellen, die eine TSQL-Sequenz emuliert. Das heißt, es wird bei jedem Aufruf immer ein ansteigender eindeutiger ganzzahliger Wert angegeben. Wenn eine Ganzzahl übergeben wird, sollte dieser Wert außerdem zurückgegeben werden, wenn noch nie ein Ergebnis größer oder die nächsthöhere verfügbare Ganzzahl vorlag. Es versteht sich von selbst, dass es mehrere Clients geben kann, die diesen SP gleichzeitig aufrufen.

Gegeben eine Tabelle MetaInfo mit den Spalten MetaKey varchar (max) und MeatValueLong bigInt. Es wird erwartet, dass die Zeile mit dem MetaKey 'Internal-ID-Last' den zuletzt zugewiesenen höchsten Wert enthält. Ich habe die folgende gespeicherte Prozedur erstellt:

CREATE PROCEDURE [dbo].[uspGetNextID]
(
  @inID bigInt 
)
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRANSACTION

    UPDATE MetaInfo WITH (ROWLOCK) 
      SET MetaValueLong = CASE 
                            WHEN ISNULL(MetaValueLong,0) > @inID THEN MetaValueLong+1 
                            ELSE @inID+1
                          END 
    WHERE MetaKey = 'Internal-ID-Last'

    SELECT MetaValueLong 
    FROM MetaInfo
    WHERE MetaKey = 'Internal-ID-Last'

    COMMIT TRANSACTION 

END

Meine Frage ist einfach, funktioniert diese gespeicherte Prozedur wie erwartet (allen Anrufern wird ein eindeutiges Ergebnis zugewiesen)?


@all: FYI, erzeugt durch dieses Q auf SO: stackoverflow.com/q/6342732/27535
gbn

Antworten:


8

Ich habe mich umgesehen und MS selbst bietet eine Lösung ohne Schlösser an

http://blogs.msdn.com/b/sqlcat/archive/2006/04/10/sql-server-sequence-number.aspx

Dies ist ein einfaches Update ohne Sperrhinweise, aber sie sagen, es sperrt / blockiert.

Darüber gibt es auch nicht viel zu berichten.

Ich würde gerne UPDLOCK zu Ihrem ROWLOCK hinzufügen (gemäß "Tabelle als Warteschlange" (SO), aber ohne READPAST). Dies erhöht die Isolation, falls ein zweiter Prozess mit dem Lesen beginnt.

Die Tatsache, dass alle Ihre Prozesse dieselbe Zeile lesen / schreiben möchten, lässt mich jedoch selbst raten. READPAST erlaubt eine sichere Parallelität, ist aber in diesem Fall unbrauchbar.

Hinweis: Sie können die OUTPUT-Klausel anstelle einer zweiten Auswahl verwenden, dann benötigen Sie die Transaktion nicht.

HTH ...


1
Du warst schneller als ich. Beachten Sie, dass SQL Server 2011 die SEQUENCE-Funktionalität enthält, sodass die Anforderung, eigene zu erfinden, bald (nicht vorzeitig) wegfallen sollte.
nvogel

@dportas: in der Tat. Und läuft auch besser: dba.stackexchange.com/q/1635/630
gbn

@dportas - Kann SEQUENCE Eingabeanforderungen berücksichtigen? Beim schnellen Lesen der Funktion habe ich diese Funktionalität nicht gesehen.
Hogan

1

Folgendes fehlt

1. SET XACT_ABORT
2. Exception Handling (Try Catch)

Ja, es sollte Ihre Bedingung erfüllen. Sobald solche Situationen in Transaktionen eintreten, werden mehrere Instanzen erstellt, und anschließend wird allen Anrufern ein eindeutiges Ergebnis zugewiesen


Wenn ich vor der Auswahl festschreibe, besteht keine Möglichkeit, dass ich das Ergebnis auswähle, das durch einen anderen Anruf gespeichert wurde?
Hogan

Ich bin mir nicht sicher. Aber scheinbar hast du recht. Ich muss das überprüfen. Vielen Dank. Übrigens +1 für, gute Frage ...

0

Eine skalierbarere Lösung, die keine Serialisierung erfordert, ist die folgende:

CREATE PROCEDURE [dbo].[uspGetNextID]
(
  @inID BIGINT OUT
)
AS
      SET NOCOUNT ON
      SET IDENTITY_INSERT SequenceTable ON;
      INSERT INTO SequenceTable (id) VALUES (@inID);
      SET IDENTITY_INSERT SequenceTable OFF;
      INSERT INTO SequenceTable DEFAULT VALUES;
      DELETE FROM SequenceTable WITH (READPAST);
      SET @inID = SCOPE_IDENTITY();
RETURN;

OP muss Benutzern erlauben, einen Wert
einzugeben,

@dportas - Danke. Mir war dieser Ansatz bekannt, aber er funktioniert hier aufgrund der Eingabeparameter nicht.
Hogan

@Hogan, ich habe meinen Vorschlag geändert, um den Eingabeparameter zu behandeln. Es ist jedoch meistens ungetestet.
nvogel

@dportas - A mit inID von 500 aufgerufen, B mit inID von 50 aufgerufen - Aktion - A gibt mit 51, B mit 52 zurück. Anforderung fehlgeschlagen.
Hogan

Interessant. Ich erhalte unterschiedliche Ergebnisse (auf 2008r2). Kannst du den vollständigen Repro mit DDL posten und deine Build / Version angeben?
nvogel
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.