1

I have stored procedure in the sql server 2008, my stored procedure calculate and get the last number "not primary key" from column from table B and add one ( +1 ) to this number to use it on the next statement on the same stored procedure.

My issue that i have a duplicate number some times, i think this happened when multiple users call the stored procedure on the same time. is this the issue and how can i solve it

my code is like the below:-

DECLARE @ID AS NVARCHAR(10)
SET @ID =   (
                SELECT TOP 1 MyNo
                FROM Employee
                WHERE (
                            (TypeID = @TypeID) AND
                            (Year = @Year)
                      )
                ORDER BY ID DESC
            )

SET @ID = ISNULL(@ID,0) + 1


INSERT INTO Employee (name,lname,MyNo) VALUES (@name,@lname,@MyNo)
  • 1
    Bad , You should have a PK on your table – Ilyes May 09 '17 at 21:33
  • This type of "roll your own" identity replacement is an anti-pattern. It is fraught with errors. And why oh why are doing math on this value and then storing it in nvarchar(10)??? You should fix your schema. Change the datatype of your column to an int and use the identity property. Anything else is going to run into issues at some point. – Sean Lange May 09 '17 at 21:37
  • thanks all. but this is a sample code not the actual code. the actual code is varcharchar(10) because the no is something like this no-year example 123-2017 and the 123 is the incremental no that i generate it and it not depends on the primary key. – Mohammad Sa'ed Naboti May 09 '17 at 21:41
  • Why not adding another identity column? – Kannan Kandasamy May 09 '17 at 22:05
  • @KannanKandasamy as i mentioned before the no is calculated depends on many conditions and types, cannot be an identity – Mohammad Sa'ed Naboti May 09 '17 at 22:06

2 Answers2

1

You can lock a table for the duration of a transaction with the WITH (TABLOCKX, HOLDLOCK) syntax:

BEGIN TRANSACTION
DECLARE @ID AS NVARCHAR(10)
SET @ID =   (
                SELECT TOP 1 MyNo
                FROM Employee WITH (TABLOCKX, HOLDLOCK)
                WHERE (
                            (TypeID = @TypeID) AND
                            (Year = @Year)
                      )
                ORDER BY ID DESC
            )

SET @ID = ISNULL(@ID,0) + 1


INSERT INTO Employee (name,lname,MyNo) VALUES (@name,@lname,@MyNo)
COMMIT TRANSACTION

You can find more information about TABLOCK and TABLOCKX here: https://learn.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-table

Per discussion, the best lock to use in this case would be:

(UPDLOCK,HOLDLOCK)
JBdev
  • 353
  • 1
  • 10
1

If you cannot use Identity column or the Table lock, another alternative is to use sp_getapplock

The advantage with this mechanism is that this kind of lock can be used across multiple stored procedures that should not run concurrently or for operations that span multiple tables. It also allows for handling timeout and other kinds of behavior if the lock is not available.

You have to be careful when using this feature and ensure you acquire and release locks properly or you will create more problems than you solve.

Manvendra Gupta
  • 406
  • 5
  • 9