Udostępnij za pośrednictwem


Numery sekwencyjne

Dotyczy:SQL ServerAzure SQL DatabaseAzure SQL Managed InstanceBaza danych SQL w usłudze Microsoft Fabric

W tym artykule wyjaśniono, jak używać numerów sekwencji w programie SQL Server, usłudze Azure SQL Database i usłudze Azure SQL Managed Instance. Sekwencja to obiekt powiązany ze schematem zdefiniowany przez użytkownika, który generuje sekwencję wartości liczbowych zgodnie ze specyfikacją, z którą utworzono sekwencję.

Przegląd

Sekwencja wartości liczbowych jest generowana w kolejności rosnącej lub malejącej w zdefiniowanym interwale i może cyklicznie (powtarzać) zgodnie z żądaniem. Sekwencje, w przeciwieństwie do kolumn tożsamości, nie są skojarzone z tabelami. Aplikacja odwołuje się do obiektu sekwencji w celu otrzymania następnej wartości. Relacja między sekwencjami i tabelami jest kontrolowana przez aplikację. Aplikacje użytkownika mogą odwoływać się do obiektu sekwencji i koordynować klucze wartości w wielu wierszach i tabelach.

Sekwencja jest tworzona niezależnie od tabel przy użyciu instrukcji CREATE SEQUENCE . Opcje umożliwiają sterowanie przyrostem, maksymalnymi i minimalnymi wartościami, punktem początkowym, możliwością automatycznego ponownego uruchamiania i buforowaniem w celu zwiększenia wydajności. Aby uzyskać informacje o opcjach, zobacz CREATE SEQUENCE.

W przeciwieństwie do wartości kolumn tożsamości, które są generowane podczas wstawiania wierszy, aplikacja może uzyskać następny numer sekwencji przed wstawieniem wiersza, wywołując funkcję NEXT VALUE FOR. Numer sekwencji jest przydzielany, gdy NEXT VALUE FOR jest wywoływany, nawet jeśli liczba nigdy nie zostanie wstawiona do tabeli. Funkcja NEXT VALUE FOR może służyć jako wartość domyślna dla kolumny w definicji tabeli. Użyj sp_sequence_get_range, aby jednocześnie uzyskać zakres wielu liczb sekwencji.

Sekwencja może być zdefiniowana jako dowolny typ danych liczb całkowitych. Jeśli typ danych nie jest określony, sekwencja jest domyślnie ustawiona na bigint.

Używanie sekwencji

Użyj sekwencji zamiast kolumn tożsamości w następujących scenariuszach:

  • Aplikacja wymaga liczby przed wstawieniem do tabeli.

  • Aplikacja wymaga udostępniania jednej serii liczb między wieloma tabelami lub wieloma kolumnami w tabeli.

  • Aplikacja musi ponownie uruchomić serię numerów po osiągnięciu określonej liczby. Na przykład po przypisaniu wartości od 1 do 10 aplikacja ponownie zacznie przypisywać wartości od 1 do 10.

  • Aplikacja wymaga sortowania wartości sekwencji według innego pola. Funkcja NEXT VALUE FOR może zastosować klauzulę OVER do wywołania funkcji. Klauzula OVER gwarantuje, że wartości zwracane są generowane w kolejności klauzuli ORDER BY klauzuli OVER.

  • Aplikacja wymaga przypisania wielu liczb jednocześnie. Na przykład aplikacja musi zarezerwować pięć liczb sekwencyjnych. Żądanie wartości tożsamości może spowodować przerwy w serii, jeśli inne procesy jednocześnie przydzielono numery. Wywołanie sp_sequence_get_range może uzyskać kilka numerów w sekwencji jednocześnie.

  • Należy zmienić specyfikację sekwencji, na przykład wartość przyrostu.

Ograniczenia

W przeciwieństwie do kolumn tożsamości, których wartości nie można zmienić, wartości sekwencji nie są automatycznie chronione po wstawieniu do tabeli. Aby zapobiec zmianie wartości sekwencji, użyj wyzwalacza aktualizacji w tabeli, aby wycofać zmiany.

Unikatowość nie jest automatycznie wymuszana dla wartości sekwencji. Możliwość ponownego użycia wartości sekwencji jest zamierzoną cechą. Jeśli wartości sekwencji w tabeli muszą być unikatowe, utwórz unikatowe ograniczenie w kolumnie. Jeśli wartości sekwencji w tabeli muszą być unikatowe w całej grupie tabel, utwórz wyzwalacze, aby zapobiec duplikatom spowodowanym przez instrukcje aktualizacji lub cykle numerów sekwencji.

Obiekt sekwencji generuje liczby zgodnie z definicją, ale obiekt sekwencji nie kontroluje sposobu użycia liczb. Numery sekwencji wstawione do tabeli mogą mieć luki, gdy transakcja jest wycofywana, gdy obiekt sekwencji jest współużytkowany przez wiele tabel lub gdy numery sekwencji są przydzielane bez ich używania w tabelach. Po utworzeniu z opcją CACHE, nieoczekiwane zamknięcie, takie jak awaria zasilania, może skutkować utratą numerów sekwencji w pamięci podręcznej.

Jeśli istnieje wiele wystąpień NEXT VALUE FOR funkcji określających ten sam generator sekwencji w ramach jednej instrukcji Transact-SQL, wszystkie te wystąpienia zwracają tę samą wartość dla danego wiersza przetworzonego przez tę instrukcję Transact-SQL. To zachowanie jest zgodne ze standardem ANSI.

Numery sekwencji są generowane poza zakresem bieżącej transakcji. Są one używane niezależnie od tego, czy transakcja przy użyciu numeru sekwencji została zatwierdzona, czy wycofana. Zduplikowana weryfikacja odbywa się tylko po pełnym wypełnieniu rekordu. Może to spowodować, że w niektórych przypadkach ta sama liczba jest używana dla więcej niż jednego rekordu podczas tworzenia, ale następnie jest identyfikowana jako duplikat. Jeśli tak się stanie i inne wartości autonumerowania zostaną zastosowane do kolejnych rekordów, może to spowodować przerwę między tymi wartościami.

Typowe użycie

Aby utworzyć liczbę całkowitą, która zwiększa się o 1 z -2 147 483 648 do 2147 483 483 647, użyj następującej instrukcji.

CREATE SEQUENCE Schema.SequenceName
    AS INT
    INCREMENT BY 1;

Aby utworzyć numer sekwencji całkowitej podobny do kolumny identyfikacyjnej, która zwiększa się o 1 od 1 do 2 147 483 647, użyj następującej instrukcji.

CREATE SEQUENCE Schema.SequenceName
    AS INT
    START WITH 1
    INCREMENT BY 1;

Zarządzanie sekwencjami

Aby uzyskać informacje o sekwencjach, wykonaj zapytanie sys.sequences.

Przykłady

Więcej przykładów można znaleźć w artykułach CREATE SEQUENCE, NEXT VALUE FOR i sp_sequence_get_range.

A. Używanie numeru sekwencji w jednej tabeli

Poniższy przykład tworzy schemat o nazwie Test, tabelę o nazwie Orders i sekwencję o nazwie CountBy1, a następnie wstawia wiersze do tabeli przy użyciu NEXT VALUE FOR funkcji .

CREATE SCHEMA Test;
GO

CREATE TABLE Test.Orders
(
    OrderID INT PRIMARY KEY,
    Name VARCHAR (20) NOT NULL,
    Qty INT NOT NULL
);
GO

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

INSERT Test.Orders (OrderID, Name, Qty)
VALUES (NEXT VALUE FOR Test.CountBy1, 'Tire', 2);

INSERT test.Orders (OrderID, Name, Qty)
VALUES (NEXT VALUE FOR Test.CountBy1, 'Seat', 1);

INSERT test.Orders (OrderID, Name, Qty)
VALUES (NEXT VALUE FOR Test.CountBy1, 'Brake', 1);

SELECT *
FROM Test.Orders;

Oto zestaw wyników.

OrderID  Name   Qty
-------- ------ ---
1        Tire    2
2        Seat    1
3        Brake   1

B. Wywołaj następną wartość parametru przed wstawieniem wiersza

Korzystając z tabeli utworzonej w przykładzie Orders A, poniższy przykład deklaruje zmienną o nazwie @nextID, a następnie używa NEXT VALUE FOR funkcji do ustawienia zmiennej na następny dostępny numer sekwencji. Aplikacja wykonuje pewne przetwarzanie zamówienia, takie jak dostarczenie klientowi numeru OrderID jego potencjalnego zamówienia, a następnie weryfikuje zamówienie. Niezależnie od tego, jak długo to przetwarzanie może potrwać lub ile innych zamówień jest dodawanych podczas procesu, oryginalny numer jest zachowywany do użytku przez to połączenie. Na koniec instrukcja INSERT dodaje kolejność do tabeli Orders.

DECLARE @NextID AS INT;
SET @NextID = NEXT VALUE FOR Test.CountBy1;

INSERT Test.Orders (OrderID, Name, Qty)
VALUES (@NextID, 'Rim', 2);

C. Używanie numeru sekwencji w wielu tabelach

W tym przykładzie przyjęto założenie, że proces monitorowania linii produkcyjnej odbiera powiadomienia o zdarzeniach występujących podczas warsztatów. Każde zdarzenie otrzymuje unikalny numer EventID, który zwiększa się w sposób monotoniczny. Wszystkie zdarzenia używają tego samego numeru sekwencji EventID, aby raporty, które łączą wszystkie zdarzenia, mogą jednoznacznie identyfikować każde zdarzenie. Jednak dane zdarzenia są przechowywane w trzech różnych tabelach, w zależności od typu zdarzenia. Przykładowy kod tworzy schemat o nazwie Audit, sekwencję o nazwie EventCounteri trzy tabele, które używają sekwencji EventCounter jako wartości domyślnej. Następnie przykład dodaje wiersze do trzech tabel i wykonuje zapytania dotyczące wyników.

CREATE SCHEMA Audit;
GO

CREATE SEQUENCE Audit.EventCounter
    AS INT
    START WITH 1
    INCREMENT BY 1;
GO

CREATE TABLE Audit.ProcessEvents
(
    EventID INT DEFAULT ( NEXT VALUE FOR Audit.EventCounter) PRIMARY KEY CLUSTERED,
    EventTime DATETIME DEFAULT (getdate()) NOT NULL,
    EventCode NVARCHAR (5) NOT NULL,
    Description NVARCHAR (300) NULL
);
GO

CREATE TABLE Audit.ErrorEvents
(
    EventID INT DEFAULT ( NEXT VALUE FOR Audit.EventCounter) PRIMARY KEY CLUSTERED,
    EventTime DATETIME DEFAULT (getdate()) NOT NULL,
    EquipmentID INT NULL,
    ErrorNumber INT NOT NULL,
    EventDesc NVARCHAR (256) NULL
);
GO

CREATE TABLE Audit.StartStopEvents
(
    EventID INT DEFAULT ( NEXT VALUE FOR Audit.EventCounter) PRIMARY KEY CLUSTERED,
    EventTime DATETIME DEFAULT (getdate()) NOT NULL,
    EquipmentID INT NOT NULL,
    StartOrStop BIT NOT NULL
);
GO

INSERT Audit.StartStopEvents (EquipmentID, StartOrStop)
VALUES (248, 0);

INSERT Audit.StartStopEvents (EquipmentID, StartOrStop)
VALUES (72, 0);

INSERT Audit.ProcessEvents (EventCode, Description)
VALUES (2735, 'Clean room temperature 18 degrees C.');

INSERT Audit.ProcessEvents (EventCode, Description)
VALUES (18, 'Spin rate threshold exceeded.');

INSERT Audit.ErrorEvents (EquipmentID, ErrorNumber, EventDesc)
VALUES (248, 82, 'Feeder jam');

INSERT Audit.StartStopEvents (EquipmentID, StartOrStop)
VALUES (248, 1);

INSERT Audit.ProcessEvents (EventCode, Description)
VALUES (1841, 'Central feed in bypass mode.');

SELECT EventID,
       EventTime,
       Description
FROM Audit.ProcessEvents
UNION
SELECT EventID,
       EventTime,
       EventDesc
FROM Audit.ErrorEvents
UNION
SELECT EventID,
       EventTime,
       CASE StartOrStop WHEN 0 THEN 'Start' ELSE 'Stop' END
FROM Audit.StartStopEvents
ORDER BY EventID;

Oto zestaw wyników.

EventID     EventTime               Description
----------- ----------------------- ------------------------------------
1           2025-12-09 17:41:57.350 Start
2           2025-12-09 17:41:57.350 Start
3           2025-12-09 17:41:57.350 Clean room temperature 18 degrees C.
4           2025-12-09 17:41:57.350 Spin rate threshold exceeded.
5           2025-12-09 17:41:57.350 Feeder jam
6           2025-12-09 17:41:57.350 Stop
7           2025-12-09 17:41:57.350 Central feed in bypass mode.

D. Generowanie powtarzających się numerów sekwencji w zestawie wyników

W poniższym przykładzie przedstawiono dwie cechy numerów sekwencji: cykliczność i użycie NEXT VALUE FOR w instrukcji select.

CREATE SEQUENCE CountBy5
    AS TINYINT
    START WITH 1
    INCREMENT BY 1
    MINVALUE 1
    MAXVALUE 5
    CYCLE;
GO

SELECT NEXT VALUE FOR CountBy5 AS SurveyGroup,
       Name
FROM sys.objects;

E. Generowanie numerów sekwencji dla zestawu wyników przy użyciu klauzuli OVER

W poniższym przykładzie użyto klauzuli OVER do sortowania zestawu wyników według Name przed dodaniem kolumny numeru sekwencji.

USE AdventureWorks2022;
GO

CREATE SCHEMA Samples;
GO

CREATE SEQUENCE Samples.IDLabel
    AS TINYINT
    START WITH 1
    INCREMENT BY 1;
GO

SELECT NEXT VALUE FOR Samples.IDLabel OVER (ORDER BY Name) AS NutID,
       ProductID,
       Name,
       ProductNumber
FROM Production.Product
WHERE Name LIKE '%nut%';

F. Resetowanie numeru sekwencji

Przykład E używał pierwszych 79 numerów sekwencji Samples.IDLabel. (Twoja wersja AdventureWorks2025 może zwrócić inną liczbę wyników). Wykonaj następujące czynności, aby użyć kolejnych 79 numerów sekwencji (od 80 do 158).

SELECT NEXT VALUE FOR Samples.IDLabel OVER (ORDER BY Name) AS NutID,
       ProductID,
       Name,
       ProductNumber
FROM Production.Product
WHERE Name LIKE '%nut%';

Wykonaj następującą instrukcję, aby ponownie uruchomić sekwencję Samples.IDLabel.

ALTER SEQUENCE Samples.IDLabel
    RESTART WITH 1;

Ponownie wykonaj instrukcję select, aby sprawdzić, czy sekwencja Samples.IDLabel została ponownie uruchomiona z numerem 1.

SELECT NEXT VALUE FOR Samples.IDLabel OVER (ORDER BY Name) AS NutID,
       ProductID,
       Name,
       ProductNumber
FROM Production.Product
WHERE Name LIKE '%nut%';

G. Zmień tabelę z tożsamości na sekwencję

W poniższym przykładzie zostanie utworzony schemat i tabela zawierająca trzy wiersze dla przykładu. Następnie przykład dodaje nową kolumnę i odrzuca starą kolumnę.

CREATE SCHEMA Test;
GO

CREATE TABLE Test.Department
(
    DepartmentID SMALLINT IDENTITY (1, 1) NOT NULL,
    Name NVARCHAR (100) NOT NULL,
    GroupName NVARCHAR (100) NOT NULL CONSTRAINT PK_Department_DepartmentID PRIMARY KEY CLUSTERED (DepartmentID ASC)
);
GO

INSERT Test.Department (Name, GroupName)
VALUES ('Engineering', 'Research and Development');
GO

INSERT Test.Department (Name, GroupName)
VALUES ('Tool Design', 'Research and Development');
GO

INSERT Test.Department (Name, GroupName)
VALUES ('Sales', 'Sales and Marketing');
GO

SELECT *
FROM Test.Department;
GO

ALTER TABLE Test.Department
    ADD DepartmentIDNew SMALLINT NULL;
GO

UPDATE Test.Department
    SET DepartmentIDNew = DepartmentID;
GO

ALTER TABLE Test.Department DROP CONSTRAINT [PK_Department_DepartmentID];
ALTER TABLE Test.Department DROP COLUMN DepartmentID;
GO

EXECUTE sp_rename 'Test.Department.DepartmentIDNew', 'DepartmentID', 'COLUMN';
GO

ALTER TABLE Test.Department ALTER COLUMN DepartmentID SMALLINT NOT NULL;

ALTER TABLE Test.Department
    ADD CONSTRAINT PK_Department_DepartmentID PRIMARY KEY CLUSTERED (DepartmentID ASC);

SELECT MAX(DepartmentID)
FROM Test.Department;

CREATE SEQUENCE Test.DeptSeq
    AS SMALLINT
    START WITH 4
    INCREMENT BY 1;
GO

ALTER TABLE Test.Department
    ADD CONSTRAINT DefSequence DEFAULT ( NEXT VALUE FOR Test.DeptSeq) FOR DepartmentID;
GO

SELECT DepartmentID,
       Name,
       GroupName
FROM Test.Department;

INSERT Test.Department (Name, GroupName)
VALUES ('Audit', 'Quality Assurance');
GO

SELECT DepartmentID,
       Name,
       GroupName
FROM Test.Department;

Transact-SQL instrukcje, które używają SELECT *, otrzymują nową kolumnę jako ostatnią kolumnę zamiast pierwszej. Jeśli nie jest to akceptowalne, musisz utworzyć zupełnie nową tabelę, przenieść do niej dane, a następnie utworzyć ponownie uprawnienia do nowej tabeli.