views:

88

answers:

3

Scenario: I have a fairly generic table (Data), that has an identity column. The data in this table is grouped (lets say by city).

The users need an identifier in order for printing on paper forms, etc. The users can only access their cites data, so if they use the identity column for this purpose they will see odd numbers (e.g. a 'New York' user might see 1,37,2028... as the listed keys.

Idealy they would see 1,2,3... (or something similar)

The problem of course is concurrency, this being a web application you can't just have something like: UserId = Select Count(*)+1 from Data Where City='New York'

Has anyone come up with any cunning ways around this problem?

Updated - from the comments below I think I want something like the SP below. Not entirely sure how the recursion should work in the CATCH block.

ALTER PROCEDURE [dbo].[DataContainer_Insert] 
@SomeData varchar(max),
@DataContainerId int out    
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;
    BEGIN TRY
        SELECT @UserId = MAX(UserId) From DataContainer
        INSERT INTO DataContainer (UserId, SomeData)
        VALUES (@UserId, SomeData)

        SELECT @DataContainerId = scope_identity()
    END TRY
    BEGIN CATCH
        --try again
        exec DataContainer_Insert @DataContainerId, @SomeData
    END CATCH       

END
+1  A: 

In a close situations if I send back from the server data produced using GROUP BY in the SELECT statement and need have something like id of every row, I use ROW_NUMBER() (see http://msdn.microsoft.com/en-us/library/ms186734.aspx). This is also good in case of data paging (see a simple example in http://weblogs.asp.net/Firoz/archive/2005/06/12/411949.aspx). Of cause this required SQL Server 2005 or higher. You don't written which SQL Server you used.

Oleg
Thanks, yea, I use the same feature for paging. But of course for paging this id is not persisted.
Grayson Mitchell
What do you mean with "paging this id is not persisted"? I show a table. To be exact, I show jqGrid filled with data and paging button inside. With a jqGrid is associated a user depended URL which correspond to complex SELECT statement with CTE (see http://www.sqlteam.com/article/server-side-paging-using-sql-server-2005). An "id" I need for data placed in jqGrid. If I use ROW_NUMBER() as id I can identify selected row in jqGrid as a corresponding row in my SELECT statement. So I have no problem.
Oleg
If I have something like the snipit below (adding an Id), I will get an Id number based on the identity column, this will return the same results as long as no columns are deleted, then when that occurs different the results for the returned id column will be different.select ROW_NUMBER() OVER (ORDER BY Id) AS 'Id', ROW_NUMBER() OVER (ORDER BY sortColumn) AS row, *
Grayson Mitchell
For such kind of identity problem **rowversion** (see http://stackoverflow.com/questions/2658443/concurrency-handling/2663654#2663654) can be very helpful.
Oleg
+1  A: 

Well, you could use your UserId = Select Count(*)+1 from Data Where City='New York' idea if you add a spanning uniqueness constraint on city and UserId (to guarantee that there would only ever be 1 instance of a particular Id and City). Then you just need to be able to handle the constraint violations (by resubmitting).

Ryan Elkins
Yea, I like this approach (rather than locking), just looking for some example code.
Grayson Mitchell
+1  A: 

As a general remark: You can do something like

newUserId = Select MAX(UserId)+1 from Data Where City='New York'
INSERT INTO data (...) VALUES (newUserId, ...)

even in a concurrent access scenario (e.g. a web application), as long as you employ proper concurrency control, i.e. (a) locking or (b) transactions with an appropriate isolation level.

See question 1994771 for an in-depth analysis of how you can do something like this with SQL Server.

Heinzi
What would that give you? A unique identifier for that particular session? What if two people from the same city are printing at the same time? What if a user is added while people are printing?
Robert Harvey
That would be the procedure for determining a new user's id (e.g, `INSERT INTO Data (UserId, ...) SELECT MAX(UserId)+1, ...`). I was just using the same notation as used in the question. I've edited my answer to clarify this.
Heinzi