적용 대상:SQL Server
Azure SQL 데이터베이스
Azure SQL Managed Instance
이 문서에서는 SQL Server, Azure SQL 데이터베이스 및 Azure SQL Managed Instance에서 시퀀스 번호를 사용하는 방법을 설명합니다. 시퀀스는 시퀀스를 만들 때 사용된 사양에 따라 숫자 값 시퀀스를 생성하는 사용자 정의 스키마 바인딩 개체입니다.
개요
숫자 값 시퀀스는 정의된 간격으로 오름차순이나 내림차순으로 생성되며 요청에 따라 순환(반복)할 수 있습니다. ID 열과 달리 시퀀스는 테이블과 연결되지 않습니다. 애플리케이션은 시퀀스 개체를 참조하여 다음 값을 받습니다. 시퀀스와 테이블 간의 관계는 애플리케이션에서 제어합니다. 사용자 애플리케이션은 시퀀스 개체를 참조하고 여러 행과 테이블에서 값 키를 조정합니다.
시퀀스는 CREATE SEQUENCE 문을 통해 테이블과 독립적으로 생성됩니다. 옵션을 사용하면 증분, 최대값 및 최소값, 시작점, 자동 다시 시작 기능 및 캐싱을 제어하여 성능을 향상시킬 수 있습니다. 옵션에 대한 자세한 내용은 CREATE SEQUENCE를 참조하세요.
행을 삽입하면 생성되는 ID 열 값과는 달리 애플리케이션에서는 NEXT VALUE FOR 함수를 호출하여 행을 삽입하기 전에 다음 시퀀스 번호를 가져올 수 있습니다. 테이블에 숫자가 삽입되지 않더라도 NEXT VALUE FOR가 호출될 때 시퀀스 번호가 할당됩니다. 테이블 정의에서 행의 기본값으로 NEXT VALUE FOR 함수를 사용할 수 있습니다. 일정 범위의 여러 시퀀스 번호를 한 번에 가져오려면 sp_sequence_get_range 를 사용합니다.
시퀀스는 모든 정수 데이터 형식으로 정의될 수 있습니다. 데이터 형식을 지정하지 않으면 시퀀스 기본값은 bigint로 설정됩니다.
시퀀스 사용
다음 시나리오에서는 ID 열 대신 시퀀스를 사용합니다.
테이블에 삽입하기 전에 애플리케이션에 숫자가 필요합니다.
애플리케이션에서 여러 테이블 또는 테이블 내의 여러 열 사이에 단일 번호 시리즈를 공유해야 하는 경우
지정된 번호에 도달하면 애플리케이션이 번호 시리즈를 다시 시작해야 하는 경우. 예를 들어 1에서 10까지 값을 할당하면 애플리케이션에서 1부터 10까지 값을 다시 할당합니다.
애플리케이션을 사용하려면 시퀀스 값을 다른 필드를 기준으로 정렬해야 합니다. NEXT VALUE FOR 함수는 함수 호출에 OVER 절을 적용할 수 있습니다. OVER 절을 사용하면 반환된 값이 OVER 절의 ORDER BY 절 순서에 따라 생성됩니다.
애플리케이션에서 여러 번호를 동시에 할당해야 하는 경우. 예를 들어 애플리케이션이 일련 번호를 다섯 개 예약해야 하는 경우가 여기에 해당합니다. ID 값을 요청하는 경우 다른 프로세스가 동시에 번호를 요청하면 시리즈에 간격이 발생할 수 있습니다.
sp_sequence_get_range
를 호출하면 시퀀스의 여러 숫자를 한 번에 검색할 수 있습니다.증분 값과 같은 시퀀스 사양을 변경해야 합니다.
제한 사항
값을 변경할 수 없는 ID 열과 달리 시퀀스 값은 테이블에 삽입된 후에 자동으로 보호되지 않습니다. 시퀀스 값이 변경되지 않게 하려면 테이블에서 업데이트 트리거를 사용하여 변경 내용을 롤백합니다.
고유성은 시퀀스 값에 자동으로 적용되지 않습니다. 시퀀스 값을 다시 사용하는 것은 의도된 것입니다. 테이블의 시퀀스 값이 고유해야 하는 경우 열에 고유한 제약 조건을 만듭니다. 테이블의 시퀀스 값이 테이블 그룹 전체에서 고유해야 하는 경우 트리거를 만들어 업데이트 문이나 시퀀스 번호 순환으로 인한 중복을 방지합니다.
시퀀스 개체는 해당 정의에 따라 숫자를 생성하지만 시퀀스 개체는 숫자가 사용되는 방식을 제어하지 않습니다. 트랜잭션이 롤백되거나 여러 테이블에서 시퀀스 개체를 공유하거나 테이블에서 시퀀스 번호를 사용하지 않고 할당할 때는 테이블에 삽입된 시퀀스 번호에 간격이 있을 수 있습니다. CACHE 옵션을 사용하여 만든 경우 정전과 같은 예기치 않은 종료로 인해 캐시에 있는 시퀀스 번호가 손실될 수 있습니다.
하나의 Transact-SQL 문 내에서 동일한 시퀀스 생성기를 지정하는 NEXT VALUE FOR 함수 인스턴스가 여러 개 있는 경우, 그 Transact-SQL 문에 의해 처리된 특정 행에 대해 모든 인스턴스가 동일한 값을 반환합니다. 이 동작은 ANSI 표준과 일치합니다.
시퀀스 번호는 현재 트랜잭션 범위 외부에서 생성되며, 시퀀스 번호를 사용하는 트랜잭션이 커밋되는지 또는 롤백되는지 여부에 관계없이 사용됩니다. 중복 유효성 검사는 레코드가 완전히 채워진 후에만 수행됩니다. 이 문제는 생성 중에 동일한 번호가 둘 이상의 레코드에 사용되지만 중복으로 식별되는 경우가 발생할 수 있습니다. 이 상황이 발생하고 다른 자동 번호 값이 후속 레코드에 적용되면 자동 번호 값 사이에 간격이 발생할 수 있습니다.
일반적인 용도
-2,147,483,648에서 2,147,483,647까지 1씩 증가하는 정수 시퀀스 번호를 만들려면 다음 문을 사용합니다.
CREATE SEQUENCE Schema.SequenceName
AS int
INCREMENT BY 1 ;
1~2,147,483,647 사이에서 1씩 증가하는 ID 열과 유사한 정수 시퀀스 번호를 만들려면 다음 문을 사용합니다.
CREATE SEQUENCE Schema.SequenceName
AS int
START WITH 1
INCREMENT BY 1 ;
시퀀스 관리
시퀀스에 대한 자세한 내용을 보려면 sys.sequences를 쿼리하십시오.
예제
CREATE SEQUENCE, NEXT VALUE FOR 및 sp_sequence_get_range 문서에서 다른 예제를 찾아볼 수 있습니다.
A. 단일 테이블에서 시퀀스 번호 사용
다음 예에서는 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);
GO
SELECT *
FROM Test.Orders;
GO
결과 집합은 다음과 같습니다.
OrderID Name Qty
1 Tire 2
2 Seat 1
3 Brake 1
B. 행을 삽입하기 전에 NEXT VALUE FOR 호출
예제 A에서 만든 Orders
테이블을 사용하면 다음 예제에서 @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);
C. 여러 테이블에서 시퀀스 번호 사용
이 예제에서는 제품군 모니터링 프로세스가 워크샵 전체에서 발생하는 이벤트에 대한 알림을 수신한다고 가정합니다. 각 이벤트는 고유하고 단조롭게 증가하는 EventID
번호를 받습니다. 모든 이벤트는 모든 이벤트를 결합하는 보고서가 각 이벤트를 고유하게 식별할 수 있도록 같은 EventID
시퀀스 번호를 사용합니다. 그러나 이벤트 데이터는 이벤트 형식에 따라 서로 다른 테이블 3개에 저장됩니다. 코드 예에서는 Audit
이라는 스키마, EventCounter
라는 시퀀스 및 EventCounter
시퀀스를 기본값으로 사용하는 세 개의 테이블을 만듭니다. 그런 다음, 예제에서는 행을 테이블 3개에 추가하고 결과를 쿼리합니다.
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 2009-11-02 15:00:51.157 Start
2 2009-11-02 15:00:51.160 Start
3 2009-11-02 15:00:51.167 Clean room temperature 18 degrees C.
4 2009-11-02 15:00:51.167 Spin rate threshold exceeded.
5 2009-11-02 15:00:51.173 Feeder jam
6 2009-11-02 15:00:51.177 Stop
7 2009-11-02 15:00:51.180 Central feed in bypass mode.
D. 결과 집합에서 반복되는 시퀀스 번호 생성
다음 예제에서는 시퀀스 번호의 두 가지 기능인 순환 및 select 문에서 NEXT VALUE FOR
사용을 보여줍니다.
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;
GO
E. 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. 시퀀스 번호 초기화
E 예제에서는 Samples.IDLabel
시퀀스 번호의 처음 79개를 사용했습니다. (AdventureWorks2022
버전은 다른 수의 결과를 반환할 수 있습니다.) 다음을 실행하여 다음 시퀀스 번호 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. 테이블을 ID에서 시퀀스로 변경
다음 예제에서는 예를 위해 스키마 하나와 행 3개가 포함된 테이블 하나를 만듭니다. 그런 다음, 예제에서는 새 열을 추가하고 이전 열을 삭제합니다.
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;
SELECT *
를 사용하는 Transact-SQL 문은 새 열을 첫 번째 열이 아니라 마지막 열로 받습니다. 허용되지 않으면 완전히 새 테이블을 만들고 데이터를 해당 테이블로 이동한 다음, 새 테이블에 대한 권한을 다시 만들어야 합니다.