views:

111

answers:

4

I simply want a stored procedure that calculates a unique id (that is separate from the identity column) and inserts it. If it fails it just calls itself to regenerate said id. I have been looking for an example, but cant find one, and am not sure how I should get the SP to call itself, and set the appropriate output parameter. I would also appreciate someone pointing out how to test this SP also.

Edit

What I have now come up with is the following (Note I already have an identity column, I need a secondary id column.

ALTER PROCEDURE [dbo].[DataInstance_Insert] 
    @DataContainerId int out,
    @ModelEntityId int,
    @ParentDataContainerId int, 
    @DataInstanceId int out
AS
BEGIN

    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    WHILE (@DataContainerId is null) 
        EXEC DataContainer_Insert @ModelEntityId, @ParentDataContainerId, @DataContainerId output   

    INSERT INTO DataInstance (DataContainerId, ModelEntityId)
    VALUES (@DataContainerId, @ModelEntityId)   

    SELECT @DataInstanceId = scope_identity()                       
END    

    ALTER PROCEDURE [dbo].[DataContainer_Insert]
        @ModelEntityId int,
        @ParentDataContainerId int,
        @DataContainerId int out
    AS
    BEGIN 
    BEGIN TRY
        SET NOCOUNT ON;
        DECLARE @ReferenceId int

        SELECT @ReferenceId = isnull(Max(ReferenceId)+1,1) from DataContainer Where ModelEntityId=@ModelEntityId

        INSERT INTO DataContainer (ReferenceId, ModelEntityId, ParentDataContainerId)
        VALUES (@ReferenceId, @ModelEntityId, @ParentDataContainerId)

        SELECT @DataContainerId = scope_identity()
    END TRY
    BEGIN CATCH
    END CATCH
    END
+1  A: 

Why not use the:

 NewId() 

T SQL function? (assuming sql server 2005/2008)

Darknight
-1 because NEWID() is a moniker for UuidCreate, which is **not** a function to allocate new SQL identity values...
Remus Rusanu
I'm not sure what you mean, I've always been able to use the 'NewId' to generate a Guid and insert this without issue?
Darknight
Also not sure what Remus meant.Instead of NewId() I normally use NewSequentialId(), which is a bit better for indexing.http://msdn.microsoft.com/en-us/library/ms189786.aspx
Jan_V
A: 

that sp will never ever do a successful insert, you have an identity property on the DataContainer table but you are inserting the ID, in that case you will need to set identity_insert on but then scope_identity() won't work

A PK violation also might not be trapped so you might also need to check for XACT_STATE()

why are you messing around with max, use scope_identity() and be done with it

SQLMenace
Not only that, what is the point of recursing with @DataContainerId passed in when that parameter is never even used?
JohnFx
+1  A: 

Better yet, why not make UserID an identity column instead of trying to re-implement an identity column manually?

BTW: I think you meant

VALUES (@DataContainerId + 1 , SomeData)
JohnFx
It is already an identity.....notice this line SELECT @DataContainerId = scope_identity()
SQLMenace
In that case, why are you even bothering to calculate the next available ID? SQL will do that for you. Just exclude the UserID column from the insert statement.
JohnFx
I am not.....I made the comment...but I didn't post this question.. :-) see my answer also
SQLMenace
Ooops, sorry. I was assuming that the PK Was another column because @DataContainerId is never used for anything.
JohnFx
Hmm, I have simplified this down a bit far, but basically what I am trying to achieve is have a User key that is a different algorithm from the identity column. Sorry that was not clear in my post (as I removed the original algorithm.
Grayson Mitchell
This post is a mess, but you left some funny comments, so am going to make you the winner
Grayson Mitchell
I see now. Normally when you find the need to have a second auto-incrementing column in the same table you've got some normalization problems. Consider adding a second table and making that column an identity, then make the column in your new table a foreign key.
JohnFx
+2  A: 
  1. In CATCH blocks you must check the XACT_STATE value. You may be in a doomed transaction (-1) and in that case you are forced to rollback. Or your transaction may had already had rolled back and you should not continue to work under the assumption of an existing transaction. For a template procedure that handles T-SQL exceptions, try/catch blcoks and transactions correctly, see Exception handling and nested transactions
  2. Never, under any languages, do recursive calls in exception blocks. You don't check why you hit an exception, therefore you don't know if is OK to try again. What if the exception is 652, read-only filegroup? Or your database is at max size? You'll re-curse until you'll hit stackoverflow...
  3. Code that reads a value, makes a decision based on that value, then writes something is always going to fail under concurrency unless properly protected. You need to wrap the SELECT and INSERT in a transaction and your SELECT must be under SERIALISABLE isolation level.

And finally, ignoring the blatantly wrong code in your post, here is how you call a stored procedure passing in OUTPUT arguments:

exec DataContainer_Insert  @SomeData, @DataContainerId OUTPUT;
Remus Rusanu
That whole thing cannot ever run even once, did you notice this line SELECT @DataContainerId = scope_identity()
SQLMenace
@SQLMenace: The whole code is like a interview puzzle 'how many mistakes can you find in these 3 lines?'
Remus Rusanu