2009-02-27 6 views
2

Wir haben eine Oracle-Anwendung, die ein Standardmuster zum Auffüllen von Ersatzschlüsseln verwendet. Wir haben eine Reihe von extrinsischen Zeilen (die spezifische Werte für die Ersatzschlüssel haben) und andere Zeilen, die intrinsische Werte haben. Wir verwenden den folgenden Oracle-Trigger-Schnipsel, um zu bestimmen, was mit dem Ersatzschlüssel auf dem Einsatz zu tun:Sequenzgeneratoren in T-SQL

IF :NEW.SurrogateKey IS NULL THEN 

SELECT SurrogateKey_SEQ.NEXTVAL INTO :NEW.SurrogateKey FROM DUAL; 

END IF; 

Wenn der gelieferte Ersatzschlüssel null ist dann einen Wert aus der nominierten Sequenz erhalten, sonst die mitgelieferten Ersatzschlüssel passieren in die Reihe.

Ich kann nicht finden, eine einfache Möglichkeit zu finden, das ist T-SQL. Es gibt alle möglichen Ansätze, von denen aber keiner den Begriff eines Sequenzgenerators wie Oracle und andere SQL-92-konforme DBs verwendet.

Kennt jemand eine wirklich effiziente Möglichkeit, dies in SQL Server T-SQL zu tun? Übrigens verwenden wir SQL Server 2008, wenn das hilfreich ist.

+0

Siehe http://dba.stackexchange.com/questions/3307/emulate-a-tsql-sequence-via-a -stored-procedure – gbn

Antwort

2

Sie können IDENTITY betrachten. Dadurch erhalten Sie eine Spalte, für die der Wert beim Einfügen der Zeile festgelegt wird.

Dies kann bedeuten, dass Sie die Zeile einfügen und den Wert anschließend mit SCOPE_IDENTITY() ermitteln müssen.

Es gibt auch einen Artikel über die Simulation von Oracle-Sequenzen in SQL Server hier: http://www.sqlmag.com/Articles/ArticleID/46900/46900.html?Ad=1

+2

Dank Matthieu, mir ist die IDENTITY-Eigenschaft für die Tabelle bekannt, aber ich versuche, sie zu vermeiden, weil ich die Flexibilität beibehalten möchte, die wir derzeit haben. Außerdem verwendet der Artikel, auf den Sie verwiesen haben, zusätzliche Tabellen und Suchvorgänge, die ich vermeiden möchte. – PaoloFCantoni

+0

Sie müssten auch eine Tabelle pro 'Sequenz' erstellen. –

0

Identität ist ein Ansatz, obwohl es eindeutige Kennungen in einer pro Tabelle Ebene generieren.

Ein anderer Ansatz besteht darin, eindeutige Bezeichner zu verwenden, insbesondere mit NewSequantialID(), die dazu führt, dass die generierte ID immer größer als die letzte ist. Das Problem bei diesem Ansatz besteht darin, dass Sie sich nicht mehr mit Ganzzahlen beschäftigen.

Die einfachste Methode zum Emulieren der Oracle-Methode besteht darin, eine separate Tabelle mit einem Zählerfeld zu erstellen und eine benutzerdefinierte Funktion zu schreiben, die dieses Feld abfragt, inkrementiert und den Wert zurückgibt.

+0

Danke Conrad, wir dachten darüber nach; aber es gibt eine besondere Anforderung, dass die Ersatzschlüssel ganze Zahlen sind. Wie ich es verstehe, sind unque Identifier GUIDs oder UUIDs. – PaoloFCantoni

0

Hier ist eine Möglichkeit, es mithilfe einer Tabelle zu tun, um Ihre letzte Sequenznummer zu speichern. Das gespeicherte Proc ist sehr einfach, die meisten Dinge drin sind, weil ich faul bin und keine Überraschungen mag, sollte ich etwas vergessen, so ... hier ist es:

----- Erstellen Sie den Sequenzwert Tabelle.

SET ANSI_NULLS ON 
GO 

SET QUOTED_IDENTIFIER ON 
GO 

CREATE TABLE [dbo].[SequenceTbl] 
(
    [CurrentValue] [bigint] 
) ON [PRIMARY] 

GO 

----------------- Erstellen Sie die gespeicherte Prozedur

SET ANSI_NULLS ON 
GO 

SET QUOTED_IDENTIFIER ON 
GO 

CREATE procedure [dbo].[sp_NextInSequence](@SkipCount BigInt = 1) 
AS 

BEGIN 

    BEGIN TRANSACTION 

    DECLARE @NextInSequence BigInt; 

    IF NOT EXISTS 
    (
     SELECT 
     CurrentValue 
     FROM 
     SequenceTbl 
    ) 

    INSERT INTO SequenceTbl (CurrentValue) VALUES (0); 

    SELECT TOP 1 
     @NextInSequence = ISNULL(CurrentValue, 0) + 1 
    FROM 
     SequenceTbl WITH (HoldLock); 

    UPDATE SequenceTbl WITH (UPDLOCK) 
     SET CurrentValue = @NextInSequence + (@SkipCount - 1); 

    COMMIT TRANSACTION 

    RETURN @NextInSequence 
END; 
GO 

-------- Verwenden die gespeicherte Prozedur in Sql Manager, um einen Testwert abzurufen.

declare @NextInSequence BigInt 

exec @NextInSequence = sp_NextInSequence; 

--exec @NextInSequence = sp_NextInSequence <skipcount>; 

select NextInSequence = @NextInSequence; 

----- Zeigt den aktuellen Tabellenwert an.

select * from SequenceTbl; 

wird Der aufmerksame bemerken, dass es ein Parameter (optional) für die gespeicherte Prozedur ist. Dadurch kann der Anrufer einen Block von IDs in der Instanz reservieren, in der der Anrufer mehr als einen Datensatz hat, der eine eindeutige ID benötigt - der Anrufer muss mit dem SkipCount nur einen einzigen Anruf tätigen, egal wie viele IDs benötigt werden. Der gesamte Block "IF EXISTS ... INSERT INTO ..." kann entfernt werden, wenn Sie daran denken, beim Erstellen der Tabelle einen Datensatz einzufügen. Wenn Sie auch daran denken, diesen Datensatz mit einem Wert einzufügen (Ihr Startwert - eine Zahl, die niemals als ID verwendet wird), können Sie auch den ISNULL (...) Teil der Auswahl entfernen und einfach CurrentValue + 1 verwenden. Nun, bevor jemand einen Kommentar macht, bitte beachten Sie, dass ich ein Software-Ingenieur bin, nicht ein dba! Also, jede konstruktive Kritik über die Verwendung von "Top 1", "Mit (HoldLock)" und "Mit (UPDLock)" ist willkommen. Ich weiß nicht, wie gut das skalieren wird, aber das funktioniert OK für mich bisher ...

+0

Konstruktive Kritik :) nicht mit diesen Sperren spielen, werden Sie verletzt ... Um Deadlocks zu vermeiden, würde ich eher empfehlen, eine Tabelle mit Identität (1,1) zu verwenden, wird die erste Operation ein Einfügen (SequenceTbl Standard einfügen Werte), erhalten Sie Ihre scope_identity(), zweite Operation ein Löschen der Zeile gleich dieser Zahl. Dies kann mit Sperren auf Zeilenebene serialisiert werden. – Rbjz