Detecting dirty reads from a stored procedure

前端 未结 8 538
自闭症患者
自闭症患者 2021-02-04 02:27

I\'ve got 100 threads that are each calling the stored procedure as defined below.

How do I prevent dirty reads?

SET QUOTED_IDENTIFIER OFF
SET ANSI_NULLS         


        
8条回答
  •  难免孤独
    2021-02-04 03:09

    Bacon Bits beat me to it, but using the OUTPUT clause will be the easiest way to work around your racing issue. Off course locking is an option too, although I think it will have slightly higher overhead. That said, using an IDENTITY column or a SEQUENCE is so much easier than trying to implement this functionality manually.

    I took the liberty of putting the answer in your code and add a few remarks:

    SET QUOTED_IDENTIFIER OFF
    SET ANSI_NULLS OFF
    GO
    
    ALTER procedure GetNextCerealIdentity(@NextKey int output,@TableID int)
    AS
    set nocount on
    
    DECLARE @RowCount int, @Err int
    DECLARE @output TABLE (NextKey int)
    
    begin transaction
    
        /*Update CfgCerealNumber Table */
        UPDATE CfgCerealNumber WITH (UPDLOCK) 
           Set CerealNumber = CerealNumber + 1
        OUTPUT inserted.CerealNumber INTO @output (NextKey)
         WHERE CerealNumberID = @TableID
    
        select @RowCount = @@RowCount, /*Obtain updated Cereal number previously incremented*/ 
               @Err = @@Error      
    
        if @Err <> 0                            /* If Error gets here then exit         */
            begin                        
                Rollback Transaction    
                raiserror ('GetNextCerealIDSeries Failed with Error: %d TableID: %d ', 16,1, @Err, @TableID)
                return -1
            end
    
        if @RowCount = 0                        /* No Record then assume table is not   */
                                        /* been initialized for TableID Supplied*/
            begin
                Rollback Transaction
                raiserror ('No Table Record Exists in CfgCerealNumber for ID:%d   ', 16,1, @TableID)
                return -1
            end
    
    COMMIT TRANSACTION
    
    
    /*Obtain updated Cereal number previously incremented*/
    SELECT @NextKey = NextKey 
     From @output
    
    return 0
    GO
    

    Remarks:

    • There is no need to do SET NOCOUNT OFF again before exiting the stored procedure. As you go out of scope this setting will return back to what it was before you entered the stored procedure.
    • I'm not sure you need the WITH (UPDLOCK), but it certainly won't hurt.
    • I kept the transaction open for as short as possible, there is no reason to fetch the value from the table-variable inside the transaction.
    • I think it's safer to first do a ROLLBACK and then do a RaisError() simply because the latter might cause the connection to be dropped by some client-software and/or you might be inside a TRY...CATCH. Both will break the flow of the commands and you'll end up with transaction count mismatch.
    • YMMV but I've always been told to use negative return codes in case of an error. Positive return-codes might be used to indicate the number of rows.. although I've never ever seen that one used in practice.

提交回复
热议问题