简体   繁体   中英

SQL Server Stored Procedure on Diferent Connection

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM