How to Create a Sequence in SQL Server

SQL Server supports sequences. Sequences enable us to create our own sequence of numeric values.

Sequences are similar to identity columns in that they generate a unique value that can be used to identify a column. However, sequences are created completely independently of any table.

When creating a sequence, we can specify how big each increment should be, and we can also make it a decrementing sequence if required.

We can also create sequences that cycle through a range. For example, we could have multiple rows with each row being assigned a number between 1 and 4. If there are more than four rows, then the sequence will start again after the fourth row.

Once we’ve created a sequence, we can then go ahead and generate sequence numbers from that sequence. The numbers generated by a sequence can be used across multiple tables or none at all if we want. Sequences can be useful for inserting unique IDs into an ID column, but we can also generate and retrieve a sequence number without inserting any rows into a table. We can use sequences to display our own unique number in the results of a SELECT query. But we can also generate a sequence number without querying a table or going anywhere near a table.

Here are examples of using T-SQL to create sequences in SQL Server.

Basic Sequence

Here’s an example of creating a simple sequence:

CREATE SEQUENCE Sequence1
    START WITH 1
    INCREMENT BY 1;

This sequence starts at 1 and increments by 1.

After running that statement, we can now use the NEXT VALUE FOR statement to generate a sequence number and return it:

SELECT NEXT VALUE FOR Sequence1;

Result:

1

We get 1 because we specified START WITH 1 when we created the sequence.

If we run NEXT VALUE FOR again, we’ll get the next number:

SELECT NEXT VALUE FOR Sequence1;

Result:

2

The reason we get 2 is because we specified INCREMENT BY 1 when we created the sequence.

It’s possible to create a sequence without specifying the START WITH and INCREMENT arguments. When we do this, SQL Server uses the default values for those arguments (and other arguments). See Create a Sequence with the Default Values later in this article for an example.

Larger Increments

We can have larger increments by specifying a larger increment in our INCREMENT BY argument.

Here’s an example of creating a sequence with a larger increment. While we’re at it, let’s also start the sequence at a different number:

CREATE SEQUENCE Sequence2
    START WITH 100
    INCREMENT BY 10;

Now let’s generate the first number:

SELECT NEXT VALUE FOR Sequence2;

Result:

100

As expected.

Now let’s generate the next number in the sequence:

SELECT NEXT VALUE FOR Sequence2;

Result:

110

It has incremented by 10 as expected.

Decrements

We can set the INCREMENT argument to a negative value in order to have a decrementing sequence:

CREATE SEQUENCE Sequence3
    START WITH 1
    INCREMENT BY -10;

SELECT NEXT VALUE FOR Sequence3;

Result:

1

Now let’s generate the next number in the sequence:

SELECT NEXT VALUE FOR Sequence3;

Result:

-9

It has decremented the start value by 10 as expected.

Specify a Minimum and/or Maximum Value

We can use the MINVALUE and MAXVALUE arguments to specify a minimum and maximum value for the sequence:

CREATE SEQUENCE Sequence4
    START WITH 100
    INCREMENT BY 10
    MINVALUE 100
    MAXVALUE 200;

Once the value gets to the MAXVALUE, any subsequent calls to NEXT VALUE FOR for this sequence will result in an error (unless the CYCLE argument is present).

Here’s an example of the error:

Msg 11728, Level 16, State 1, Line 1
The sequence object 'Sequence4' has reached its minimum or maximum value. Restart the sequence object to allow new values to be generated.

The same concept applies to MINVALUE, except that it applies when the minimum value is exceeded.

We have several options for overcoming this error including, changing MAXVALUE (or MINVALUE if decrementing), resetting the sequence, or using the CYCLE argument (below).

The CYCLE Argument

We can use the CYCLE argument to overcome the error produced in the previous example (although it won’t be suitable in many cases). This argument specifies whether the sequence object should restart from the minimum value (or maximum for descending sequence objects). In other words, instead of stopping at the maximum value, it starts again from the minimum value.

CREATE SEQUENCE Sequence5
    START WITH 1
    INCREMENT BY 1
    MINVALUE 1
    MAXVALUE 3
    CYCLE;

Now let’s incorporate that sequence into the result set of a query that returns multiple rows from a database table:

SELECT 
    NEXT VALUE FOR Sequence5 AS CustomerGroup,
    CustomerName
FROM Customers;

Result:

CustomerGroup  CustomerName      
-------------  ------------------
1              Palm Pantry       
2              Tall Poppy        
3              Crazy Critters    
1              Oops Media        
2              Strange Names Inc.

5 row(s) returned

We can see that the sequence reached its MAXVALUE and then started again at the MINVALUE.

Let’s run the query again:

SELECT 
    NEXT VALUE FOR Sequence5 AS CustomerGroup,
    CustomerName
FROM Customers;

Result:

CustomerGroup  CustomerName      
-------------  ------------------
3              Palm Pantry       
1              Tall Poppy        
2              Crazy Critters    
3              Oops Media        
1              Strange Names Inc.

5 row(s) returned

Bear in mind that when the CYCLE argument results in the count starting again, the count starts at the MINVALUE (not the START WITH value).

To demonstrate this, let’s create another sequence that starts at 2 instead of 1:

CREATE SEQUENCE Sequence6
    START WITH 2
    INCREMENT BY 1
    MINVALUE 1
    MAXVALUE 3
    CYCLE;

Now let’s incorporate that sequence into the result set of a query that returns multiple rows from a database table:

SELECT 
    NEXT VALUE FOR Sequence6 AS CustomerGroup,
    CustomerName
FROM Customers;

Result:

CustomerGroup  CustomerName      
-------------  ------------------
2              Palm Pantry       
3              Tall Poppy        
1              Crazy Critters    
2              Oops Media        
3              Strange Names Inc.

5 row(s) returned

Caching

By default, the sequence is cached. This increases performance for applications that use sequence objects by minimising the number of disk IOs that are required to generate sequence numbers.

However, we can also specify a cache size by using the CACHE argument followed by the desired cache size. If we don’t specify a size, the database engine will determine the cache size.

We can also specify NO CACHE if desired.

Create a Sequence with the Default Values

It’s possible to create a sequence without specifying any arguments. In this case, the default values will be used for all properties.

Here’s an example:

CREATE SEQUENCE Sequence7;

That created a sequence called Sequence7 with default values. We can now use the sys.sequences system catalog view to check the properties of the sequence:

SELECT
    TYPE_NAME(system_type_id) AS "Type",
    start_value,
    increment,
    minimum_value,
    maximum_value,
    is_cycling,
    is_cached,
    current_value
FROM sys.sequences 
WHERE name = 'Sequence7';

Result:

Type    start_value           increment  minimum_value         maximum_value        is_cycling  is_cached  current_value       
------  --------------------  ---------  --------------------  -------------------  ----------  ---------  --------------------
bigint  -9223372036854775808  1          -9223372036854775808  9223372036854775807  false       true       -9223372036854775803

Specify the Data Type

By default, sequence numbers use the bigint data type. However, we can specify any integer type (including user-defined types).

Here’s an example of specifying a different data type:

CREATE SEQUENCE Sequence8 AS tinyint;

Now let’s check the sequence with sys.sequences:

SELECT
    TYPE_NAME(system_type_id) AS "Type",
    start_value,
    increment,
    minimum_value,
    maximum_value,
    current_value
FROM sys.sequences 
WHERE name = 'Sequence8';

Result:

Type     start_value  increment  minimum_value  maximum_value  current_value
-------  -----------  ---------  -------------  -------------  -------------
tinyint  0            1          0              255            0            

In this case, we specified a tinyint type, and we can see how this affects the default values of the various properties.