当数据库上的负载很重时,max(column_name)返回相同的值

时间:2015-01-14 11:32:41

标签: sql-server

以下查询不断被点击并且记录将被插入到表"TRANSACTION_MAIN"中,但是@confrm这个数字超过max(TRN_CNFRM_NBR)对于几个事务是相同的,这种行为是可见的仅当DataBase服务器上的负载过高时。对此有任何见解,为什么会观察到这种行为,幕后可能会发生什么?

            BEGIN TRANSACTION
            DECLARE @Confrm as int;
            SET @Confrm = (SELECT isnull
            (MAX(CONVERT(int, TRN_CNFRM_NBR)),0) 
            FROM TRANSACTION_MAIN WHERE
            TRN_UC_LOC = @1) + 1;

            DECLARE @TMID as int;

            INSERT INTO TRANSACTION_MAIN(
                TRN_CNFRM_NBR
                ,TRN_UC_LOC
                ,TRN_STAT_ANID
                ,TRN_SRC_ANID
                ,PRSN_ANID
                ,TRN_DT
                ,TRN_ACTL_AMT
                ,TRN_MTHD
                ,TRN_IP_ADDR
                ,DSCT_CD
                ,TRN_PAID_AMT
                ,CSHR_ANID
                ,INVDEPTEQUIP_ANID
                ,TRN_CMNT
                ,CPN_DSCT_TOTAL
            )
            VALUES(@Confrm,@1,@2,@3,@4,@5,@6,@7,@8,@9,@10,@11,@12,@13,@14);

            SET @TMID = @@IDENTITY;");

               COMMIT TRANSACTION

2 个答案:

答案 0 :(得分:1)

嗯,这不安全,所以它不会令人感到惊讶,因为它不起作用。

最终结果有一些贡献者。首先,同时选择最大值当然会返回相同的值,因为" new"行尚未插入。其次,根据事务隔离级别,select不会看到已插入但尚未提交的行。

作为一个快速解决方案,它应该有助于简单地将事务隔离级别设置得更高。这当然会降低吞吐量并增加死锁的风险,但至少它是正确的。或者,如果您在SQL服务器上足够高,请使用序列。或线程安全的CLR代码。

如果你被困在旧的SQL服务器上并且无法处理更高的事务隔离,你可以尝试使用自己的序列来实现它,其中递增序列是一个原子操作。这是Microsoft SQL Server Migration Assistant for Oracle中的一个很好的例子。

答案 1 :(得分:0)

你错误地添加了1。 查询应该是这样的:

SET @Confrm = (
        SELECT ISNULL(MAX(CONVERT(INT, TRN_CNFRM_NBR)), 0) + 1 -- you should add 1 here.
        FROM   TRANSACTION_MAIN
        WHERE  TRN_UC_LOC = @1
    );