带有dateadd的T-SQL存储过程失败

时间:2018-08-21 16:38:53

标签: sql sql-server race-condition dateadd

我的存储过程中看似简单的一部分似乎失败了,它是一个日期比较。我正在寻找超过5分钟的最早订单以开始处理它。

该存储过程由python程序调用,以获取订单号并开始对其进行处理。每次触摸订单都会修改o.chg_date,可能会延长5分钟的延迟时间。在订单创建过程中,有时销售人员将合并两个订单。他们需要5分钟的时间才能做到这一点。

这是存储过程的简化版本,但是其他where子句的典型版本。

CREATE PROCEDURE [dbo].[Get_Next_Order] 
AS
    SELECT TOP 1 o.order_id
    FROM orders o (NOLOCK)
    WHERE
        o.order_state = 4               -- ready to be processed
        AND o.order_status <> 3         -- not a deleted order
        AND o.items > 0                 -- not an empty order
        AND o.chg_date < DATEADD(mi, -5, GETDATE())     -- 5 minute delay after ready
    ORDER BY
        o.chg_date

问题:一打订单中,有两个将在5-10秒内得到处理。

我不正确使用DATEADD函数吗?它确实有80%的时间工作。而且,仅通过在SSMS中运行代码就无法使其失败。

我唯一想到的另一件事是存在order_state = 4chg_date尚未更新的竞争状况。关于种族条件解决的建议吗?

order_state通过代码更新。 chg_date由触发器更新。

1 个答案:

答案 0 :(得分:0)

if object_id(N'TempDB.dbo.#orders') is not null
    drop table #orders;

--this time is too new (papa bear)
Select
    1 As Order_id
    ,4 As Order_state
    ,1 As Order_status
    ,11 As Items
    ,sysdatetime() As Chg_date
into #orders
union All
--this time is too old (momma bear)
Select
    2 As Order_id
    ,4 As Order_state
    ,1 As Order_status
    ,11 As Items
    ,dateadd(hour,-1,sysdatetime()) As Chg_date
union all
--this time is just right (baby bear)
Select
    3 As Order_id
    ,4 As Order_state
    ,1 As Order_status
    ,11 As Items
    ,dateadd(minute,-6,sysdatetime()) As Chg_date;

-----------------------only copy bottom section into your stored proc-----------------------------
with top_order as
(
    Select
        order_id
        ,Chg_date
        ,row_number() over (order by chg_date desc) as Row_num
    From
        #orders O (Nolock)
    Where
        O.Order_state = 4                   -- ready to be processed
        And O.Order_status <> 3             -- not a deleted order
        And O.Items > 0                     -- not an empty order
        and O.Chg_date <= dateadd(minute,-5,sysdatetime())
)

select order_id
from Top_order
where row_num = 1;