views:

91

answers:

5

I'm trying to make an even simpler function than the one described here to get the next value of an order number for a shopping cart.

  • I don't care if there are gaps
  • Only completed orders get an ID (i.e. I'm deliberately not using IDENTITY)
  • Obviously there must not be duplicates
  • I don't care about performance and locking. If we have so many new orders that I care about lockin then I'll have other problems first

I've found quite a few other similar questions, but not the exact solution i'm looking for.

What I have so far is this :

USE [ShoppingCart]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Sequence_CompletedOrderID]
([val] [int] NOT NULL 
  CONSTRAINT [DF_Sequence_CompletedOrderID_NextValue]  DEFAULT ((520000))
) ON [PRIMARY]

then for the stored proc :

CREATE PROC dbo.GetNextCompletedOrderId 
  @nextval AS INT OUTPUT
AS

UPDATE dbo.sequence_completedorderid SET @nextval=val += 1;
GO

Like I said I'm trying to base it on the article I linked to above - so perhaps this just a clumsy way of doing it. My SQL isn't quite up to much for even simple things like this, and its past my bedtime. Thanks!

A: 

How about using the following statement after inserting data in your table?

UPDATE dbo.sequence_completedorderid
SET @nextval = (SELECT MAX(val) + 1 FROM dbo.sequence_completedorderid)
Greco
how is tis goign to be handling concurrency?? You'll get lots of duplicated values in a highly stressed system - really really bad idea.....
marc_s
you have no protection against several client transactions accessing the table "dbo.Sequence_CompletedOrderID) at the same time and all getting the same "next value" from that table. Works fine if you only have one client - causes nothing but grief when you have hundreds....
marc_s
I know about SCOPE_IDENTITY(), but the requestor didn't use identity columns...
Greco
+1  A: 

OK, so you already have an IDENTITY column in your main table - but how about just having an additional table with again has an IDENTITY column?? This would save you so much trouble and hassle.....

USE [ShoppingCart]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Sequence_CompletedOrderID]
([val] [int] NOT NULL IDENTITY(520000, 1)
) ON [PRIMARY]



CREATE PROC dbo.GetNextCompletedOrderId 
  @nextval AS INT OUTPUT
AS
   INSERT INTO dbo.Sequence_CompletedOrderID DEFAULT VALUES

   SELECT @nextval = SCOPE_IDENTITY()
GO

That way, you can leave all the hassle of making sure things are unique etc. to SQL Server, and it will also make sure you won't ever get back the same value twice from the IDENTITY column!

marc_s
I'd agree, but extend this a teeny bit and stick an order number column in too - yes its notionally redundant but it gives you a bit of resiliency for free.
Murph
@murph and/or an audit datetime field. might help in diagnosing missing values by having a datetime in there too - plus it justifies having a table with just a single value in it
Simon_Weaver
@marc_s is there an easy way to have the value just updated rather than creating a new row every time? nto that i'm too bothered either way.
Simon_Weaver
@Simon: if you want to have just a single value and keep updating it, then your approach with the regular INT field and just getting the next value inside a stored proc should be fine.
marc_s
A: 

You don't need a new id column, all you need is to add a new OrderCompleted column (bit), and combine that with the id you already have.

SELECT Id FROM T_Order WHERE OrderCompleted = 1
Mark Byers
but i want a nice sequence so when a human is looking at the orders printed out in a pile they dont see any gaps
Simon_Weaver
adn yet the first statement in your list of requirements is: **I don't care if there are gaps**
marc_s
ok so i really meant - if due to unexpected circumstances in my application layer i get a gap that is fine, but i primarily want a sequence that will look good on screen to a layperson
Simon_Weaver
+1  A: 

The solution from @marc_s creates a new row for each number generated. At first I didn't think I liked this, but realized I can use it to my advantage.

What I did was added a date time audit column, and also an @orderid parameter to the stored proc. For a particular orderid it will be guaranteed to return the same completedorderid, which is the number from the sequence generator.

If for some reason my application layer requests the next id, but then crashes before it can commit the transaction - it will still be linked to that order so that when it is requested again the same number will be returned.

This is what I ended up with:

USE [ShoppingCart]
GO
/****** Object:  Table [dbo].[Sequence_CompletedOrderID]    Script Date: 11/29/2009 03:36:40 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Sequence_CompletedOrderID](
    [val] [int] IDENTITY(520000,1) NOT NULL,
    [CreateDt] [datetime] NOT NULL CONSTRAINT [DF_Sequence_CompletedOrderID_CreateDt]  DEFAULT (getdate()),
    [Orderid] [int] NOT NULL CONSTRAINT [DF_Sequence_CompletedOrderID_Orderid]  DEFAULT ((0)),
 CONSTRAINT [PK_Sequence_CompletedOrderID] PRIMARY KEY CLUSTERED 
(
    [Orderid] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]


USE [ShoppingCart]
GO
/****** Object:  StoredProcedure [dbo].[GetCompletedOrderId]    Script Date: 11/29/2009 03:34:08 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROC [dbo].[GetCompletedOrderId] 
  @orderid AS INT,
  @completedorderid AS INT OUTPUT
AS

IF EXISTS (SELECT * FROM dbo.Sequence_CompletedOrderID WHERE orderid = @orderid)
BEGIN 
    SET @completedorderid =(SELECT val FROM dbo.Sequence_CompletedOrderID WHERE orderid = @orderid)
END 
ELSE
BEGIN
   INSERT INTO dbo.Sequence_CompletedOrderID (orderid) VALUES (@orderid)
   SET @completedorderid =(SELECT SCOPE_IDENTITY())
END
Simon_Weaver
from an business logic point of view this seems to work well too, once you've decided an order number should be issues you generate it and are guaranteed it is static - even if something funky goes on. it is being generated outside the scope of a transaction and therefore i need to be careful. i'm generating the order number when a user gets to the confirm page. that way we can email them if they don't commit - and have already got an order number to give them for future reference. if they dont get that far the order doesnt get a number
Simon_Weaver
A: 

If you use the Sequence_CompletedOrderID table as a one row table of order IDs then you should use UPDATE and rely on the OUTPUT clause to capture the new value:

CREATE PROC dbo.GetNextCompletedOrderId 
  @nextval AS INT OUTPUT
AS
SET NOCOUNT ON;
UPDATE dbo.Sequence_CompletedOrderID
SET val=val + 1
OUTPUT @nextval = INSERTED.val;
GO
Remus Rusanu