【问题标题】:Turn off implicit transactions关闭隐式事务
【发布时间】:2010-10-26 14:17:48
【问题描述】:

我想使用 select 进行插入操作,但我知道某些行可能会失败(这是意料之中的)。有没有办法关闭 SQL Server 2008 的隐式事务,让没有失败的事务不回滚?

-- get the count of the customers to send the sms to
SELECT @count = COUNT(*)
FROM PreCampaignCustomer
WHERE Processed = 0 AND CampaignID = @campaignid 
AND ErrorCount < 5

WHILE (@count > 0)
BEGIN
    DECLARE @customerid INT,
            @carrierid INT,
            @merchantcustomerid INT,
            @smsnumber NVARCHAR(50),
            @couponcode NVARCHAR(20)

    SELECT TOP 1 @customerid = pcc.CustomerID, @merchantcustomerid = pcc.MerchantCustomerID,
    @carrierid = c.CarrierID, @smsnumber = c.SMSNumber 
    FROM PreCampaignCustomer pcc
    INNER JOIN Customer c ON c.ID = pcc.CustomerID
    WHERE pcc.Processed = 0 AND pcc.CampaignID = @campaignid
    AND pcc.ErrorCount < 5
    ORDER BY pcc.ErrorCount

    --set the couponcode    
    IF @couponlength = -1 
    BEGIN
        SET @couponcode = 'NOCOUPON'
    END
    ELSE
    BEGIN
        EXEC [GenerateCouponCode]
        @length = 9,
        @couponcode = @couponcode OUTPUT
    END

    BEGIN TRY
        --use try/catch just in case the coupon code is repeated or any other error

        --Set the coupon text
        DECLARE @coupontext NVARCHAR(200),
                @smsrequestxml XML


        IF @coupontypecode = 1 --NONE
        BEGIN 
            SET @coupontext = @merchantname + ': ' + @smsmessage + ', Use Code: ' + dbo.FormatCouponCode(@couponcode, @couponcodegrouping) + '. Reply STOP to quit'
        END
        ELSE
        BEGIN
            SET @coupontext = @merchantname + ': ' + @smsmessage + '. Reply STOP to quit'
        END

        EXEC GetSMSRequest @config = @smsconfig, 
                    @smsType = 1, --Submit
                    @address = @smsnumber,
                    @carrierID = @carrierid,
                    @message = @coupontext,
                    @xml = @smsrequestxml OUTPUT

        BEGIN TRAN
            --create the CampaignCustomer record
            INSERT INTO CampaignCustomer
            (CampaignID, CustomerID, CouponCode, Active)
            VALUES
            (@campaignid, @customerid, @couponcode, 1)

            --Add the record to the queue
            INSERT INTO SMSQueue
            (CarrierID, [Address], [Message], TimeToSend, RequestXML, QueueID, HTTPStatusCode, Retries)
            VALUES
            (@carrierid, @smsnumber, @coupontext, @timetosend, @smsrequestxml, @queueid, 0, 0)

            --Create Outgoing SMS Log
            INSERT INTO SMSOutgoingLog
            (MerchantID, MerchantGroupID, MessageTypeCode, CreatedDate, Active)
            VALUES 
            (@merchantid, @merchantgroupid, @messagetypecode, GETDATE(), 1)

            --Update the LastSentSMSTime of the MerchantCustomer
            UPDATE MerchantCustomer
            SET LastSentSMS = GETDATE(),
            ModifiedDate = GETDATE()
            WHERE ID = @merchantcustomerid

            UPDATE PreCampaignCustomer
            SET Processed = 1,
            ModifiedDate = GETDATE()
            WHERE CustomerID = @customerid 
            AND CampaignID = @campaignid
        COMMIT TRAN
    END TRY
    BEGIN CATCH
        ROLLBACK TRAN

        -- Set the error
        UPDATE PreCampaignCustomer
        SET ErrorCount = ErrorCount + 1,
        ModifiedDate = GETDATE(),
        ErrorMessage = ERROR_MESSAGE(),
        ErrorNumber = ERROR_NUMBER()
        WHERE CustomerID = @customerid 
        AND CampaignID = @campaignid
    END CATCH

    SELECT @count = COUNT(*)
    FROM PreCampaignCustomer
    WHERE Processed = 0 AND CampaignID = @campaignid 
    AND ErrorCount < 5
END

【问题讨论】:

    标签: sql-server sql-server-2008


    【解决方案1】:

    不,INSERT 是单个命令。事务控制如何将多个命令组合成单个工作单元,而不是控制行在单个命令中的组合方式。你不能有一些行 INSERT 和那些失败的行(一些约束问题)而被忽略。如果任何行失败,则整个 INSERT 失败。

    为什么不尝试修改 SELECT 以排除将失败的行?

    类似:

    INSERT INTO YourTable
            (col1, col2, col3)
        SELECT
            colA, colB, ColC
            FROM YourOtherTable
            WHERE ColA NOT IN (SELECT col1 FROM YourTable)
    

    【讨论】:

    • 我为每一行生成一个随机的优惠券代码,并且优惠券代码有一个唯一的约束。我没有选择查看它是否存在,而是让 SQL Server 处理约束并让它失败。您认为使用该优惠券代码检查 count(*) 会更快吗?
    • 您是否在循环,每次迭代插入一个?如果是这样,请发布代码,以便我可以确切地看到发生了什么。有一些方法可以生成一组随机数:stackoverflow.com/questions/183124/…。 COUNT(*) 也比普通的 EXISTS 慢(它做更多的工作)。
    • 是的,我正在循环播放。我添加了代码。底线我需要使这个查询尽可能高效。也许不是在每次迭代中获取计数,而是执行 select top 1 并执行 WHILE NOT @customerid 为空。还有其他建议吗?
    • 这段代码有什么问题?我没有看到 INSERT+SELECT?您最初提出的问题。看起来你一直在尝试,直到你的插入工作。
    • 我正在努力使其尽可能高效。 insert select 可以做到这一点,但如果失败,就没有办法做到这一点,这就是我采用这种方法的原因。
    【解决方案2】:

    开箱即用,如果您使用 SSIS 来执行此操作,您可以将失败的行发送到不同的路径,或者直接将它们丢弃。

    【讨论】:

      【解决方案3】:

      您可能正在考虑唯一索引的 IGNORE_DUP_KEY 属性。

      请参阅 IGNORE_DUP_KEY 上的 this related SO questionthe official MSDN article

      您必须使用 ALTER INDEX 来添加此属性,或者(如果唯一约束在主键上)删除并重新创建它。

      一旦完成,任何插入都应该只拒绝无效行而不是整个批次。

      【讨论】:

      • 有没有办法获取插入后失败的行?
      • 我们决定不采用此解决方案,因为它会影响非聚集索引的性能。我们确实利用它来分配预定义的优惠券代码。谢谢...
      【解决方案4】:

      可以使用 SET XACT_ABORT OFF (ON) 控制交易行为 -- more here

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-12-24
        • 2019-06-18
        • 1970-01-01
        • 1970-01-01
        • 2012-11-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-22
        相关资源
        最近更新 更多