Lee posted on January 15, 2012 11:14
Sequences in SQL Server 2012 are new, but sequences have been an ANSI standard for a long time. I first worked with sequences in Oracle 8.0 back in 2001. Applications back then in SQL Server 6.5 and 7 oftentimes created a sequence table; each time you wanted an ‘identity’, you went to a homespun sequence table and added the value 1, for instance, to the current value, and then inserted. Oftentimes you had to do it in a certain way so that another transaction didn’t insert the same sequence number, or attempt to any way. So for instance, you fetch a sequence from a table, insert the value, and then go back and update the sequence table with the value that was inserted. Not terribly efficient or stealthy-sounding, huh? So enter Identity and all of its wonder and problems.

And, for SQL Server 2012, back to the future with Sequences. I’ve been dorking with them this morning, please follow along. I’ve created a database called Company:

CREATE SCHEMA Orders; 
GO  
DROP TABLE Orders.TblOrders;
GO
CREATE TABLE Orders.TblOrders
   (PKOrderID int PRIMARY KEY
    ,FKCustomerID varchar(40) NOT NULL
    ,OrderQuantity int
    ,OrderDate datetime2 NOT NULL DEFAULT 

GETDATE()

);
GO  

CREATE SEQUENCE Orders.SqnceTblOrders     
START WITH 1     

INCREMENT

 BY 1;
GO   

I’ve created a schema, table, and a sequence with the above. Check Books Online for all of the sequence options. The essentials for making this thing work appear to be START WITH and INCREMENT BY.

INSERT INTO

 Orders.TblOrders (PKOrderID, FKCustomerID, OrderQuantity)    
    VALUES (NEXT VALUE FOR Orders.SqnceTblOrders, 1, 133); 

INSERT INTO

 Orders.TblOrders (PKOrderID, FKCustomerID, OrderQuantity )  
    VALUES (NEXT VALUE FOR Orders.SqnceTblOrders, 1, 345); 

INSERT INTO

 Orders.TblOrders (PKOrderID, FKCustomerID, OrderQuantity)    
    VALUES (NEXT VALUE FOR Orders.SqnceTblOrders, 1, 111); 
GO  
 
SELECT * FROM Orders.TblOrders; 
GO

 

image

 

First thing that I thought of was…remove the NEXT VALUE and simply throw it in a declare:

DECLARE @seqValue int = NEXT VALUE FOR Orders.SqnceTblOrders

INSERT INTO

 Orders.TblOrders (PKOrderID, FKCustomerID, OrderQuantity)    
    VALUES (@seqValue, 1, 111); 
GO  

SELECT * FROM Orders.TblOrders; GO

 

image

 

One thing that I don’t like is that sequence works like IDENTITY for values in a transaction; if it’s rolled back, when you insert again, it picks back up with the next value. Nope, don’t like that behavior. Run the following:

BEGIN TRANSACTION
    

INSERT INTO

 Orders.TblOrders (PKOrderID, FKCustomerID, OrderQuantity)    
    VALUES (NEXT VALUE FOR Orders.SqnceTblOrders, 1, 133); 
ROLLBACK

INSERT INTO

 Orders.TblOrders (PKOrderID, FKCustomerID, OrderQuantity)    
    VALUES (NEXT VALUE FOR Orders.SqnceTblOrders, 1, 133); 

SELECT * FROM Orders.TblOrders; 
GO

image

 

If, however, you implement the above declare you could possibly save the value if some sort of retry logic is maintained:

DECLARE @seqValue int = NEXT VALUE FOR Orders.SqnceTblOrders

BEGIN TRANSACTION trnOrderInsert
    
    INSERT Orders.TblOrders (PKOrderID, FKCustomerID, OrderQuantity)    
        VALUES (@seqValue, 1, 111); 

    SELECT * FROM Orders.TblOrders; 

    ROLLBACK TRAN trnOrderInsert

INSERT INTO

Orders.TblOrders (PKOrderID, FKCustomerID, OrderQuantity)    
        VALUES (@seqValue, 1, 111); 

SELECT * FROM Orders.TblOrders; 
GO

 

How about more than one on a table? Looks like it works…

ALTER TABLE Orders.TblOrders
    ADD RowNumber INT;

CREATE SEQUENCE Orders.SqnceRowNumber     
START WITH 1     
INCREMENT BY 1;
GO  

DECLARE @seqValue int = NEXT VALUE FOR Orders.SqnceTblOrders;
DECLARE @RowNumber int = NEXT VALUE FOR Orders.SqnceRowNumber;

INSERT Orders.TblOrders (PKOrderID, FKCustomerID, OrderQuantity, RowNumber)    
        VALUES (@seqValue, 1, 111, @RowNumber);
GO

SELECT * FROM Orders.TblOrders;
GO    
 
image


Maybe as a constraint:

CREATE SEQUENCE Orders.SqnceSequence3     
START WITH 1     

INCREMENT

 BY 1;
GO  

ALTER TABLE Orders.TblOrders
    ADD SequenceWithConstraint INT DEFAULT NEXT VALUE FOR Orders.SqnceSequence3;
GO

Now insert some data…looks like as a default constraint might be a cool way to throw a sequence in, but again this will lose a bit of control if you are going to use it with multiple tables via a variable as shown above.

DECLARE @seqValue int = NEXT VALUE FOR Orders.SqnceTblOrders;
DECLARE @RowNumber int = NEXT VALUE FOR Orders.SqnceRowNumber;

INSERT Orders.TblOrders (PKOrderID, FKCustomerID, OrderQuantity, RowNumber)    
        VALUES (@seqValue, 1, 111, @RowNumber);
GO

SELECT * FROM Orders.TblOrders;

GO
 
image
 
 

One thing to consider maybe…it appears that there is no checking or pairing for a sequence – Books online states the following:

Sequences, unlike identity columns, are not associated with specific tables. 

This means that outside of an application you better darn sure be careful which sequence you’re calling, because you can call whichever sequence that you may have previously created:

DROP TABLE #Test
GO
CREATE TABLE #Test
    (ID int)
GO
INSERT INTO #Test (ID) VALUES (NEXT VALUE FOR Orders.SqnceTblOrders);
GO
SELECT * FROM #Test;
GO

 

There’s a big section in BOL with caching. Maybe we’ll look at that one next week.

Thanks for reading,

Lee

 

-----------------------------

Sequences and IDENTITY…have to keep giving Joe Celko something to bitch about, right?

 

 


Posted in: SQL Server 2012 , TSQL  Tags:
blog comments powered by Disqus

by Lee Everest, M.S.

Info

Poll

Do you use Azure or cloud in your organization?



Show Results

Ads

Search


Month List

Calendar

«  May 2012  »
MoTuWeThFrSaSu
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910
View posts in large calendar

Tags

Disclaimer
The opinions, code, examples, et.al. expressed herein are my own personal opinions and do not represent my employer's view in any way, shape form, or fashion.  All code for demonstration purposes - no guarantees, either written or implied, are made.

© Copyright 2012 Lee Everest's SQL Server, etc. weblog