T-SQL Ninja #26

Sequence

Was sind SEQUENCEs?

Eine der wichtigsten SEQUENCEs hatten wir euch bereits im letzten Randori vorgestellt. Generell ist ein SEQUENCE-Objekt die Verallgemeinerung des IDENTITY-Attributs. Denn während das IDENTITY-Attribut dafür sorgt, dass jede neue Zeile eine neue Zahl zugewiesen bekommt, ist das SEQUENCE-Objekt ein Objekt, das Reihen von Zahlen erzeugt. Dabei ist es anders als das IDENTITY-Attribut nicht an ein anderes Datenbankobjekt (also eine Tabelle) gebunden, sondern ist ein eigenes Objekt, das abgefragt und verwendet werden kann.

Wie könnt ihr mit SEQUENCEs arbeiten?

Zunächst müsst ihr euch ein SEQUENCE-Objekt in einem neuen Schema definieren. Das tut ihr mit dem CREATE SEQUENCE-Befehl:

CREATE SCHEMA Test
GO

CREATE SEQUENCE Test.SeqCount 
    START WITH 1
    INCREMENT BY 1;
GO

Nun habt ihr also das SEQUENCE-Objekt erzeugt, die Parameter waren ähnlich wie bei der IDENTITY-Eigenschaft der Startwert (also eins) und das Inkrement, also der Wert um den jeweils erhöht wird (also wieder eins). Um euch den jeweils nächsten Wert aus der Sequenz zu holen, führt einige Male das folgende Statement aus:

SELECT NEXT VALUE FOR Test.SeqCount

Ihr seht, dass bei jeder Abfrage der Wert um eins steigt. Anders als bei der IDENTITY-Eigenschaft einer Spalte habt ihr hier aber noch andere Optionen beim Anlegen einer Sequenz. Legen wir nun also eine zweite Sequenz an:

CREATE SEQUENCE Test.SeqCountMax
    START WITH 1
    INCREMENT BY 1
    MINVALUE 1
    MAXVALUE 5;
GO

Dieser Sequenz habt ihr neben dem Startwert auch einen Minimalwert (der in diesem Fall gleich dem Startwert ist) und einen Maximalwert mitgegeben. Holt ihr euch nun sechsmal den nächsten Wert der Sequenz, so überschreitet sie den Maximalwert:

SELECT NEXT VALUE FOR Test.SeqCountMax

Ihr erhaltet bei den ersten fünf Aufrufen die Werte 1 bis 5 zurück, beim sechsten Aufruf aber eine Fehlermeldung, dass die Sequenz ihren Maximalwert erreicht hat. Was kann dagegen getan werden? Einfach: anders als bei der Identity-Eigenschaft können Sequenzen so erstellt werden, dass sie Werte „recyceln“:

CREATE SEQUENCE Test.SeqCountCycle
    START WITH 1
    INCREMENT BY 1
    MINVALUE 1
    MAXVALUE 5
    CYCLE;
GO

Wenn ihr nun sechsmal den nächsten Wert abruft, so erhaltet ihr beim sechsten Aufruf keinen Fehler, sondern wieder den Wert 1:

SELECT NEXT VALUE FOR Test.SeqCountCycle

Ihr könnt aber nicht nur auf den Startwert zurück springen, sondern auch auf andere Werte, beispielsweise können wir in einer Sequenz auch negative Werte ausgeben:

CREATE SEQUENCE Test.SeqCountCycleNeg
    START WITH 1
    INCREMENT BY 1
    MINVALUE -5
    MAXVALUE 5
    CYCLE;
GO

Ruft ihr nun sechsmal den nächsten Wert der Sequenz ab, so erhaltet ihr beim sechsten Aufruf den Wert -5, der dann bis +5 durch läuft um dann wieder zu -5 zu springen:

SELECT NEXT VALUE FOR Test.SeqCountCycleNeg

Im Übrigen kann euer INCREMENT BY auch negativ sein, dann zählt die Sequenz die Werte nicht nach oben, sondern runter, ihr könnt also eine Art „Countdown“ bauen…

Doch nun zum Nutzen von Sequenzen. Wirklich praktisch werden Sequenzen, wenn ihr eine Art „Schlüssel“ über mehrere Tabellen definieren müsst. Um das zu demonstrieren, legen wir uns eine Sequenz und zwei Tabellen an, die diese Sequenz als Standardwert für ihre Id-Spalten verwenden:

DROP SEQUENCE IF EXISTS Test.SeqSharedKey;
CREATE SEQUENCE Test.SeqSharedKey
    START WITH 1
    INCREMENT BY 1;

DROP TABLE IF EXISTS Test.SucceededRuns
DROP TABLE IF EXISTS Test.FailedRuns

CREATE TABLE Test.SuccededRuns 
(
     RunId int NOT NULL DEFAULT (NEXT VALUE FOR Test.SeqSharedKey)
    ,RunName varchar(250) 
)

CREATE TABLE Test.FailedRuns 
(
     RunId int NOT NULL DEFAULT (NEXT VALUE FOR Test.SeqSharedKey)
    ,RunName varchar(250) 
)

Nun fügen wir Zeilen ein und fragen die Tabellen ab:

INSERT INTO Test.SuccededRuns (RunName)
VALUES ('First Succeeded'), ('Second Succeeded')

INSERT INTO Test.FailedRuns (RunName)
VALUES ('First Failed')

INSERT INTO Test.SuccededRuns (RunName)
VALUES ('Third Succeeded')

SELECT 
     RunId
    ,RunName 
FROM Test.SuccededRuns

SELECT 
     RunId
    ,RunName 
FROM Test.FailedRuns

Wir sehen, dass in Test.SucceededRuns die Runs mit den RunIds 1, 2 und 4 zu finden sind, während der Eintrag mit der RunId 3 in Test.FailedRuns eingetragen wurde.

Wichtig zu beachten ist dabei, dass hier aber keine Konsistenz gewährleistet ist, da es sich bei der RunId-Spalte nicht um eine Identity-Spalte handelt, könnten wir hier beliebige Werte eintragen, auch Duplikate oder Fantasiewerte. Anders als bei einer Spalte mit IDENTITY-Eigenschaft würdet ihr das nicht einmal merken, da ihr nicht gezwungen seid, einen expliziten IDENTITY_INSERT durchzuführen, sondern einfach in die Spalte schreiben könnt und anders als bei einer IDENTITY-Spalte würde die Sequenz auch nicht „merken“ dass hier schon ein höherer Wert eingetragen ist und dort fortsetzen sondern einfach stur beim nächsten Wert der Sequenz fortfahren. In solchen Fällen wäre also über die Applikation eine Sachgemäße Verwendung zu garantieren, da das Datenbanksystem sie nicht unbedingt garantieren kann.

Referenzen

Ninja-Notebooks @ GitHub

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.