【问题标题】:How to avoid NULL value in IF EXISTS statement within transaction?如何避免事务中 IF EXISTS 语句中的 NULL 值?
【发布时间】:2021-03-01 18:48:01
【问题描述】:

我有数千个线程同时执行。 第一种线程是这样的T-SQL查询:

DECLARE @import_id int

IF EXISTS (
        SELECT [id]
        FROM [dbo].[Import]
        WHERE created IS NULL
        )
    SELECT @import_id = [id]
    FROM [dbo].[Import]
    WHERE created IS NULL
ELSE
BEGIN
    INSERT INTO [dbo].[Import] ([startdate])
    SELECT GETDATE()

    SELECT @import_id = @@IDENTITY
END

第二种线程是这样的T-SQL查询:

UPDATE [dbo].[Import] SET created = GETDATE() WHERE created IS NULL

所有这些类型的 T-SQL 查询在 事务 中执行,类型为 Read Commited

问题:有时,第一个查询会返回 @import_id = NULL,但我希望 @import_id 有一些价值。

问题是Read Commited事务类型如何避免这种情况?

【问题讨论】:

    标签: sql-server multithreading tsql concurrency transactions


    【解决方案1】:

    这似乎是一个奇怪的设计,但我想你已经遗漏了很多。

    解决此问题的一种方法是删除IF 测试,因为数据似乎在IF 测试和SELECT 之间发生变化。所以改为:

    SELECT @import_id = [id]
    FROM [dbo].[Import]
    WHERE created IS NULL;
    
    IF @import_id IS NULL BEGIN
        INSERT INTO [dbo].[Import] ([startdate])
        SELECT GETDATE();
    
        SELECT @import_id = SCOPE_IDENTITY(); -- SCOPE_IDENTITY() is recommended over @@IDENTITY
    END;
    

    请注意,您可能需要 TOP 1ORDER BY 作为您的 SELECT

    当然,如图所示,您可能会获得一个@import_id,其中created is null,但您的其他线程甚至可以在@import_id 返回给客户端之前将其设置为GETDATE()。但我想你已经想到了。

    【讨论】:

    • 非常感谢。您的解决方案解决了我的问题。我有几个相关的问题:1.如果我在select语句中添加WITH (UPDLOCK, HOLDLOCK)提示可以吗(为了更可靠)? 2. 当我添加ORDER BY 时,我遇到了死锁(有TOP 1 而没有ORDER BY 一切正常)。
    • 感谢您对SCOPE_IDENTITY() 的建议。这很重要。
    • @Phil WITH (UPDLOCK, HOLDLOCK) 可能有助于防止其他线程在您返回之前对其进行更改 - 但它可能会在几分之一秒后更改它,因此这实际上取决于这些记录在更广泛的背景。关于TOP 1 ORDER BY,我只能说,没有它你会从正确的行中选择吗?您需要首先确保行为正确,然后解决出现的任何问题。因此,如果没有 order by 的行为是正确的,请不要更改它。
    • 我明白了。感谢您的帮助和详细的回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多