Storing a counter- take 2 (original post has been hi-jacked)

  • I need to sequentially number events created by users with an incremental counter. In the events table, I have a unique key on the combination of OrgId, TypeId, and OrgEventCounter. Are there any “best practices” on how best to handle the unlikely possibility that 2 users will try to save at the same time with the same OrgEventCounter?

    I could trap for :

    Msg 2627, Level 14, State 1, Line 9

    Violation of UNIQUE KEY constraint 'IX_DocDataEvent'. Cannot insert duplicate key in object 'dbo.DocDataEvent'.

    and loop until it works, but it seems there should be a more elegant way.

    Another thought using recusion (cringe)- but given the low likelihood that this scenario will even be encountered, would the following be reasonable?

    /*

    Let's say that this code lives in a stored proc called

    uspEventInsert (@OrgId, @TypeId)

    */

    BEGIN TRY

    DECLARE @OrgEventCounter int

    SET @OrgEventCounter =

    (SELECT MAX(OrgEventCounter)

    FROM dbo.Event

    WHERE OrganizationID = @OrgId AND TypeID = @TypeId) + 1

    INSERT dbo.Event

    (OrganizationID, TypeID, OrgEventCounter)

    VALUES (OrgId, TypeId, @OrgEventCounter)

    END TRY

    BEGIN CATCH

    IF (ERROR_NUMBER() = 2627)

    EXEC uspEventInsert @OrgId, @TypeId

    END CATCH

    🙂

  • Hi Stef

    There are various suggestions in the other thread. What are you missing?

    Greets

    Flo

  • The couple of replies focused on using an IDENTITY field, which i can't do since the counter value is different per Org/Type. So is the solution I present above reasonable? Something about it feels questionable but it is simple and effective, at least in the testing I ran with it.

  • [font="Verdana"]Either of the approaches I outlined should still work.

    Already posted answer

    [/font]

  • I'm sorry Bruce, I guess I did a bad job of posting the problem to begin with. I have no trigger, so your suggestion is:

    -- if there is no trigger on your Event table, then use the following

    -- logic to get the next counter

    --

    update dbo.Event

    set @OrgEventCounter = OrgEventCounter = OrgEventCounter + 1

    where OrganizationID = 2325 and

    TypeID = 1

    The OrgEventCounter is not in a separate table, it's only stored as a field in the Event record. The only thing i'm trying to avoid is 2 users saving an event at the very same moment with the same OrgEventId.

  • The only thing i'm trying to avoid is 2 users saving an event at the very same moment with the same OrgEventId.

    Can you just create a Unique Constraint ?


    * Noel

  • Hello Stef

    Here a complete example how to use a id table:

    -- Event table

    IF (OBJECT_ID('tempdb..#event') IS NOT NULL)

    DROP TABLE #event

    GO

    CREATE TABLE #event (org_id INT, type_id INT, id INT)

    GO

    -- Id table

    IF (OBJECT_ID('tempdb..#id_tab') IS NOT NULL)

    DROP TABLE #id_tab

    GO

    CREATE TABLE #id_tab (table_name SYSNAME, next_id INT)

    GO

    INSERT INTO #id_tab VALUES ('Event', 1)

    GO

    -- ID procedure

    IF (OBJECT_ID('tempdb..#usp_get_id') IS NOT NULL)

    DROP PROCEDURE #usp_get_id

    GO

    CREATE PROCEDURE #usp_get_id

    @table_name SYSNAME,

    @id INT = NULL OUTPUT

    AS

    BEGIN TRANSACTION

    UPDATE #id_tab SET next_id = next_id + 1 WHERE table_name = @table_name

    SELECT @id = next_id - 1 FROM #id_tab

    COMMIT TRANSACTION

    RETURN @id

    GO

    -- insert event procedure

    IF (OBJECT_ID('tempdb..#usp_insert_event') IS NOT NULL)

    DROP PROCEDURE #usp_insert_event

    GO

    CREATE PROCEDURE #usp_insert_event

    @org_id INT,

    @type_id INT

    AS

    DECLARE @id INT

    EXECUTE #usp_get_id 'Event', @id OUTPUT

    INSERT INTO #event VALUES (@org_id, @type_id, @id)

    GO

    -- Sample values

    EXECUTE #usp_insert_event 1, 2

    EXECUTE #usp_insert_event 1, 2

    EXECUTE #usp_insert_event 2, 2

    EXECUTE #usp_insert_event 2, 2

    GO

    -- Result

    SELECT * FROM #event

    Greets

    Flo

  • Yes, I do have a unique constraint on OrgId + TypeId + OrgEventCounter. But I'm just trying to handle it gracefully. I don't want the save attempt fail- I just need to try again with the next available counter.

  • florian.reischl (3/5/2009)


    BEGIN TRANSACTION

    UPDATE #id_tab SET next_id = next_id + 1 WHERE table_name = @table_name

    SELECT @id = next_id - 1 FROM #id_tab

    COMMIT TRANSACTION

    Heh... yeah... we had code like that.... caused an average of 640 deadlocks per day. I'll try to come back tonight and show you how we fixed that problem.

    By the way... You're not running "CostGuard" are you, Flo?

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • Hi Jeff

    This was only a simple sample. Sorry for performance problems due lockins on the id-table 😉

    In real implementation wi work with a temporary table for scripts and procedures to maintain caches of new ids. The client applications we use other ways to get a bunch of new ids (e.g. 100) for the client in one step and work with this. So locks should be really rarely.

    But

    What is "CostGuard"?? I don't know this.

    Greets

    Flo

  • "CostGuard" is a telephone accounting system. It had similar code to the NEXT_ID example you posted and it was, in fact, called "NEXT_ID".

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

Viewing 11 posts - 1 through 10 (of 10 total)

You must be logged in to reply to this topic. Login to reply