I have a stored procedure that gets a unique reference from a table. You pass in a code eg 'I' for itemref or 'T' for tranref there are other codes, then the stored procedure will look up the table, increment the number and return said number (this is so other people can have the next number etc.).
However at the object level (vb.net) I wanted to get the reference on different connection that would never lock the table by accessing it in the middle of a transaction. This worked great until while testing I hit some code where a different stored procedure (while in a transaction posting an invoice) also called the get_ref stored procedure, this was fine until the object tried getting it again and then locked the system.
Is there a way for one stored procedure to call another just on a different connection/transaction level?
This is the get_ref stored procedure:
CREATE PROCEDURE get_ref
(@Ref NUMERIC(28,0) OUTPUT,
@RefType VARCHAR(8),
@AddVal NUMERIC(28,0) = NULL)
AS
SET NOCOUNT ON
DECLARE @VALINC int
IF @Addval IS NULL
SET @VALINC = 1
ELSE
IF @Addval > 0
SET @VALINC = @Addval
ELSE
SET @VALINC = 1
IF UPPER(@RefType) = 'HM'
BEGIN
BEGIN TRANSACTION
UPDATE HMREFS
SET REF = REF + @valinc
IF (@@ERROR <> 0)
GOTO ERRLAB
SELECT @ref = REF
FROM HMREFS
IF (@@ERROR <> 0)
GOTO ERRLAB
COMMIT TRANSACTION
END
ELSE
IF UPPER(@RefType) = 'T'
BEGIN
BEGIN TRANSACTION
UPDATE sysgen
SET TRANREF = TRANREF + @valinc
IF (@@ERROR <> 0)
GOTO ERRLAB
SELECT @ref = TRANREF FROM sysgen
IF (@@ERROR <> 0)
GOTO ERRLAB
COMMIT TRANSACTION
END
ELSE
IF UPPER(@RefType) = 'I'
BEGIN
BEGIN TRANSACTION
UPDATE sysirgen
SET ITEMREF = ITEMREF + @valinc
IF (@@ERROR <> 0)
GOTO ERRLAB
SELECT @ref = ITEMREF FROM sysirgen
IF (@@ERROR <> 0)
GOTO ERRLAB
COMMIT TRANSACTION
END
RETURN 0
ERRLAB:
ROLLBACK TRANSACTION
RAISERROR ('Error Getting Reference', 16, 1)
RETURN 1
GO
I have my program which has two connections to the database. Connection1 for getting references etc. this will never use a begin transaction as I do not want to block any other user from accessing the same tables. Connection2 for doing the work.
My program will get a reference for an invoice on Connection1, then on Connection2 begin a transaction and do the work of positing said invoice in SP1. However as it involves stock (it may not in some cases) SP1 has to call the SP get_ref so it can insert a stock movement line. This then locks the table that the stored procedure get_ref accessed until Connection2 is committed. However the invoice is posted and SP1 returns to the program. Before my program commits Connection2 it has to dispatch the the goods (if there are any) and it stays in the transaction. It then calls for a reference on Connection1 which is locked as the table it wants was accessed by SP1 on Connection2. Then the program crashes.
Currently and the way it used to work is all the work was on the same connection therefore no deadlocking issue except when we involve long running processes and other people doing the same. In which case we will get a deadlock issue with someone else logged in. Its not an issue we hit all that frequently however it is something we would like to tidy up. Hence trying to access the get_ref tables on a different transaction.
It would be helpful to see some code calling this procedure but you have multiple transactions going on inside here. I would suggest reworking this to utilize TRY/CATCH. It makes the code a lot simpler to follow. It is also quite possible that you had a transaction get hung up which is what causes your locking previously.
Something like this.
CREATE PROCEDURE get_ref
(
@Ref numeric(28,0) OUTPUT
, @RefType VARCHAR(8)
, @AddVal numeric(28,0) = NULL
) AS
SET NOCOUNT ON;
DECLARE @VALINC int
--rewritten as a case expression below
--IF @Addval IS NULL
-- SET @VALINC = 1
--ELSE
-- IF @Addval > 0
-- SET @VALINC = @Addval
-- ELSE
-- SET @VALINC = 1
select @VALINC = case when isnull(@AddVal, 0) = 0 OR @AddVal < 0 then 1 else @AddVal end
BEGIN TRY
BEGIN TRANSACTION
IF UPPER(@RefType) = 'HM'
BEGIN
UPDATE HMREFS SET REF = REF + @valinc
SELECT @ref = REF FROM HMREFS
END
ELSE
IF UPPER(@RefType) = 'T'
BEGIN
UPDATE sysgen SET TRANREF = TRANREF + @valinc
SELECT @ref = TRANREF FROM sysgen
END
ELSE
IF UPPER(@RefType) = 'I'
BEGIN
UPDATE sysirgen SET ITEMREF = ITEMREF + @valinc
SELECT @ref = ITEMREF FROM sysirgen
END
COMMIT TRANSACTION
--there is no point in returning 0, that is the default return value when a procedure completes without error
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
RAISERROR ('Error Getting Reference', 16, 1)
RETURN 1
END CATCH
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.