• subscribe
January 26, 2009 12:00 AM

T-SQL Puzzle – Custom Sequence

SQL Server Pro
InstantDoc ID #101338

There are certain cases where you need a sequence generator and the IDENTITY property is not an option. One such case is when your sequence must guarantee no gaps (e.g., with systems that do not allow gaps in invoice IDs). The IDENTITY property doesn’t guarantee no gaps since an increment of the identity value isn’t undone when the INSERT that caused it to increment fails. There may be other reasons why you would want to use an alternative sequence generator instead of the IDENTITY property. The T-SQL Puzzle involves creating such a custom sequence generator in SQL Server 2008.

Run the following code to create the Invoices and Sequence tables:

SET NOCOUNT ON;

USE tempdb;

 

IF OBJECT_ID('dbo.Invoices', 'U') IS NOT NULL

  DROP TABLE dbo.Invoices;

GO

CREATE TABLE dbo.Invoices

(

  invoiceid INT NOT NULL,

  custid    INT NOT NULL,

  invdate   DATE NOT NULL,

  amt       MONEY NOT NULL,

  CONSTRAINT PK_Invoices PRIMARY KEY(invoiceid)

);

 

IF OBJECT_ID('dbo.Sequence', 'U') IS NOT NULL

  DROP TABLE dbo.Sequence;

GO

CREATE TABLE dbo.Sequence(val INT NOT NULL);

INSERT INTO dbo.Sequence(val) VALUES(0);

 

The Invoices table holds invoice information, and the Sequence table holds the last used invoice ID. Your task is to write a stored procedure that accepts details of a new invoice as input, increments the sequence value in the Sequence table by 1, and uses the new value along with the invoice info provided as input to insert a new row into the Invoices table. The procedure should return the newly generated invoice ID as an output parameter. Here’s the header of the stored procedure:

CREATE PROC dbo.InsertInvoice

  @custid    AS INT,

  @invdate   AS DATE,

  @amt       AS MONEY,

  @invoiceid AS INT OUTPUT

AS

 

<your statement goes here>;

 

The challenge: do it all using a single statement! That is, the stored procedure’s body must be made of a single statement that does three things:

1.    Increments the sequence value in the Sequence table

2.    Inserts the new invoice row (with the newly generated invoice ID) into the Invoices table

3.    Assigns the new sequence value to the output parameter @invoiceid

You can use the following code to test your procedure:

DECLARE @newinvid AS INT;

 

EXEC dbo.InsertInvoice

  @custid    = 1,

  @invdate   = '20090212',

  @amt       = 1000.00,

  @invoiceid = @newinvid OUTPUT;

 

SELECT @newinvid;

I’ll post the solution in a new entry this Friday.

Good luck!

BG

 



ARTICLE TOOLS

Comments
  • Franz
    3 years ago
    Feb 12, 2009

    In the post of plamen, there is an sql fragment that looks like this:
    update dbo.Sequence
    set @invoiceid = val + 1
    ,val += 1;

    This statement assumes that the fields are updated exactly in the specified order. Is this order guaranteed by the SqlServer (SQL-Ansi standard) or is this an assumption that may or may not hold??
    If the sqlserver rearranges the order of the fields' assignments (val +=1 first, @invoiceid=val+1 second), then we were out of luck.

    A similar question would be: can I write a select statement like this? :
    declare @i int;
    select
    @i=5
    ,@i=@i+3;

    With other wording: is the order of the assignments guaranteed, so that I can trust @i equals 8, or could it be that @i equals 5 ?

  • Plamen
    3 years ago
    Jan 26, 2009

    Here is SQL Server 2008 solution using composable DML:

    INSERT INTO Invoices(invoiceid, custid, invdate, amt)
    SELECT invoiceid, @custid, @invdate, @amt
    FROM (UPDATE dbo.Sequence
    SET @invoiceid = val + 1, val += 1
    OUTPUT Inserted.val) AS T(invoiceid);

    [Itzik: Yes, that's the one I was hoping to see. Well done! BTW, I also got a similar solution privately from Davide Mauri.]

  • Steve
    3 years ago
    Jan 26, 2009

    This seems to do the job:

    UPDATE dbo.Sequence SET
    @invoiceid = val += 1
    OUTPUT INSERTED.val, @custid, @invdate, @amt
    INTO dbo.Invoices
    FROM dbo.Sequence

    [Itzik: As you already noticed, the FROM clause is not necessary, but it's a valid solution of course. Thanks!]

  • Guillermo
    3 years ago
    Jan 26, 2009

    Maybe

    UPDATE Sequence SET @invoiceid = val = val+1
    OUTPUT inserted.val, @custid,@invdate, @amt INTO Invoices (invoiceid, custid, invdate, amt)

    [Itzik: Well done navento! This is a valid solution according to the puzzle’s requirements. For those who would like to continue working on the puzzle, there’s a new option in SQL Server 2008 that allows achieving this task using a single statement. Can you figure out what it is?]

You must log on before posting a comment.

Are you a new visitor? Register Here