Поделиться через


Порядковые номера

Применимо к:SQL ServerБаза данных SQL AzureУправляемый экземпляр SQL AzureБаза данных SQL в Microsoft Fabric

В этой статье объясняется, как использовать порядковые номера в SQL Server, Базе данных SQL Azure и Управляемом экземпляре SQL Azure. Последовательность представляет собой определяемый пользователем объект, привязанный к схеме, который формирует последовательность числовых значений в соответствии со спецификацией, с которой эта последовательность создавалась.

Обзор

Последовательность числовых значений создается в порядке возрастания или убывания с определенным интервалом и может зацикливаться, если это необходимо. Последовательности, в отличие от столбцов идентификаторов, не связаны с таблицами. Приложение обращается к объекту последовательности, чтобы получить следующее значение. Приложения управляют связями между последовательностями и таблицами. Пользовательские приложения могут ссылаться на объект последовательности и координировать ключи значений между несколькими строками и таблицами.

Последовательность создается независимо от таблиц с помощью инструкции CREATE SEQUENCE . Параметры позволяют управлять приращением, максимальным и минимальным значением, начальной точкой, возможностью автоматического перезапуска и кэшированием для повышения производительности. Сведения о параметрах см. в разделе CREATE SEQUENCE.

В отличие от значений столбцов идентификаторов, которые создаются при вставке строк, приложение может получить следующий порядковый номер до вставки строки, вызвав функцию NEXT VALUE FOR . Порядковый номер выделяется, когда вызывается NEXT VALUE FOR, даже если число никогда не вставляется в таблицу. Функцию NEXT VALUE FOR можно использовать в качестве значения по умолчанию для столбца в определении таблицы. Используйте sp_sequence_get_range, чтобы сразу получить диапазон нескольких порядковых номеров.

Последовательность может быть определена с любым типом данных integer. Если тип данных не указан, последовательность по умолчанию принимает значение bigint.

Использование последовательностей

Используйте последовательности вместо столбцов идентификаторов в следующих сценариях.

  • Приложению требуется номер до выполнения вставки в таблицу.

  • Приложению требуется делиться единым рядом чисел между несколькими таблицами или столбцами в таблице.

  • Приложение должно перезапускать последовательность номеров по достижении определенного номера. Например, после назначения значений от 1 до 10 приложение вновь начинает назначать значения от 1 до 10.

  • Приложению необходимо сортировать значения последовательности по другому полю. Функция NEXT VALUE FOR может применить клаузу OVER к вызову функции. Предложение OVER гарантирует, что возвращаемые значения создаются в порядке OVER предложения ORDER BY.

  • Приложению требуется одновременно назначать несколько номеров. Например, приложению требуется зарезервировать пять последовательных номеров. Запрос значений идентификаторов может привести к пробелам в последовательности, если другие процессы одновременно запрашивают номера. Вызов sp_sequence_get_range может одновременно получить несколько чисел в последовательности.

  • Необходимо изменить спецификацию последовательности, например значение приращения.

Ограничения

В отличие от столбцов идентификаторов, значения которых нельзя изменить, значения последовательности не защищены автоматически после вставки в таблицу. Чтобы запретить изменение значений последовательности, используйте в таблице триггер Update для отката изменений.

Уникальность не применяется автоматически для значений последовательности. Возможность повторного использования значений последовательностей предусмотрена специально. Если значения последовательности в таблице должны быть уникальными, создайте уникальное ограничение для столбца. Если значения последовательности должны быть уникальными в пределах группы таблиц, создайте триггеры для исключения повторов, вызываемых инструкциями обновлений или циклической сменой порядковых номеров.

Объект последовательности создает числа в соответствии с его определением, но объект последовательности не управляет использованием чисел. В порядковых номерах, вставляемых в таблицу, могут возникать промежутки в случае отката транзакции, если объект последовательности совместно используется несколькими таблицами или если порядковые номера выделяются, но не используются в таблицах. При создании с CACHE параметром неожиданное завершение работы, например сбой питания, может привести к потере порядковых номеров в кэше.

Если в одной инструкции Transact-SQL есть несколько экземпляров NEXT VALUE FOR функции, указывающей один и тот же генератор последовательности, все эти экземпляры возвращают одно и то же значение для заданной строки, обработанной этим оператором Transact-SQL. Такое поведение согласуется со стандартом ANSI.

Порядковые номера создаются вне области текущей транзакции. Они используются, фиксируются ли транзакции с использованием последовательного номера или отката. Проверка на наличие повторов выполняется только после того, как наполнение записи полностью завершено. Это может привести к случаям, когда одно и то же число используется для создания более чем одной записи, но впоследствии выявляется как дубликат. Если это произошло и к последующим записям были применены другие значения автосчетчика, это может привести к различиям в значениях автосчетчика.

Типичное использование

Чтобы создать целочисленный порядковый номер с приращением 1, меняющийся от -2 147 483 648 до 2 147 483 647, используйте следующую инструкцию.

CREATE SEQUENCE Schema.SequenceName
    AS INT
    INCREMENT BY 1;

Чтобы создать последовательность целых чисел, аналогичную столбцу идентичности, увеличивающуюся на 1 от 1 до 2 147 483 647, используйте следующий запрос.

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

Управление последовательностями

Чтобы получить сведения о последовательностях, запросите представление sys.sequences.

Примеры

В статьях CREATE SEQUENCE, NEXT VALUE FOR и sp_sequence_get_range приведены дополнительные примеры.

А. Использование порядкового номера в одной таблице

В следующем примере создается схема с именем Test, таблица с именем Orders и последовательность с именем CountBy1, а затем вставляет строки в таблицу с помощью NEXT VALUE FOR функции.

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;

Вот результаты.

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

B. Вызов следующего значения перед вставкой строки

Orders Используя таблицу, созданную в примере A, следующий пример объявляет переменную с именем@nextID, а затем использует NEXT VALUE FOR функцию для задания переменной следующего доступного номера последовательности. Предполагается, что в приложении выполняется некоторая обработка заказа, например заказчику сообщается номер OrderID потенциального заказа, а затем проводится проверка заказа. Независимо от времени, затрачиваемого на такую обработку, и от числа других заказов, добавляемых во время обработки, исходный номер сохраняется для использования в этом соединении. Наконец, инструкция INSERT добавляет заказ в таблицу Orders .

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

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

В. Использование номера последовательности в нескольких таблицах

В этом примере предполагается, что процесс мониторинга производственной линии получает уведомления о событиях, происходящих в цеху. Каждое событие получает уникальный, монотонно возрастающий номер EventID . Все события используют один порядковый номер EventID , и поэтому отчеты, где объединяются все события, могут однозначно определить каждое событие. Данные событий хранятся в трех различных таблицах в зависимости от типа события. В примере кода создается схема с именем Audit, последовательность с именем EventCounterи три таблицы, каждая из которых использует последовательность EventCounter в качестве значения по умолчанию. Затем в примере добавляются строки в три таблицы и запрашиваются результаты.

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;

Вот результаты.

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. Создание повторяющихся порядковых номеров в результирующем наборе

В следующем примере показаны две возможности работы с порядковыми номерами: циклическое повторение и использование NEXT VALUE FOR в инструкции 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;

Е. Генерация последовательных номеров для результирующего набора с помощью предложения OVER

В следующем примере используется предложение OVER, чтобы отсортировать результирующий набор по столбцу Name перед добавлением колонки с порядковым номером.

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. Сброс номера последовательности

В примере Е использованы первые 79 порядковых номеров Samples.IDLabel. (Ваша версия AdventureWorks2025 может возвращать другое количество результатов.) Выполните следующую команду, чтобы использовать следующие 79 порядковых номеров (с 80 по 158).

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

Выполните следующую инструкцию, чтобы перезапустить последовательность Samples.IDLabel

ALTER SEQUENCE Samples.IDLabel
    RESTART WITH 1;

Снова выполните инструкцию SELECT, чтобы убедиться, что последовательность Samples.IDLabel перезапущена с номера 1.

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

G. Изменение таблицы от использования идентификатора к последовательности

В следующем примере создается схема и таблица, содержащая три строки. Затем в примере добавляется новый столбец и удаляется старый столбец.

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 инструкции, которые используют SELECT *, получают новый столбец как последний, а не как первый столбец. Если это недопустимо, необходимо создать совершенно новую таблицу, переместить данные в нее, а затем повторно создать разрешения для новой таблицы.