更新表而不使用游标和日期

Kas*_*hif 4 sql t-sql sql-server sql-server-2005 sql-server-2008

请复制并运行以下脚本

DECLARE @Customers TABLE (CustomerId INT)
DECLARE @Orders TABLE ( OrderId INT, CustomerId INT, OrderDate DATETIME )
DECLARE @Calls TABLE (CallId INT, CallTime DATETIME, CallToId INT, OrderId INT)
-----------------------------------------------------------------
INSERT INTO @Customers SELECT 1
INSERT INTO @Customers SELECT 2
INSERT INTO @Customers SELECT 3
-----------------------------------------------------------------
INSERT INTO @Orders SELECT 10, 1, DATEADD(d, -20, GETDATE())
INSERT INTO @Orders SELECT 11, 1, DATEADD(d, -10, GETDATE())
INSERT INTO @Orders SELECT 12, 2, DATEADD(d, -8, GETDATE())
INSERT INTO @Orders SELECT 13, 2, DATEADD(d, -6, GETDATE())
INSERT INTO @Orders SELECT 14, 3, DATEADD(d, -4, GETDATE())
-----------------------------------------------------------------
INSERT INTO @Calls SELECT 101, DATEADD(d, -19, GETDATE()), 1, NULL
INSERT INTO @Calls SELECT 102, DATEADD(d, -17, GETDATE()), 1, NULL
INSERT INTO @Calls SELECT 103, DATEADD(d, -9, GETDATE()), 1, NULL
INSERT INTO @Calls SELECT 104, DATEADD(d, -6, GETDATE()), 1, NULL
INSERT INTO @Calls SELECT 105, DATEADD(d, -5, GETDATE()), 1, NULL
INSERT INTO @Calls SELECT 106, DATEADD(d, -4, GETDATE()), 2, NULL
INSERT INTO @Calls SELECT 107, DATEADD(d, -2, GETDATE()), 2, NULL
INSERT INTO @Calls SELECT 108, DATEADD(d, -2, GETDATE()), 3, NULL
Run Code Online (Sandbox Code Playgroud)

我想更新@Calls表并需要以下结果.

替代文字

我使用以下查询(回答之前的旧查询)

UPDATE  @Calls
SET     OrderId = ( 
                    CASE 
                        WHEN (s.CallTime > e.OrderDate)
                          THEN e.OrderId
                    END                                 
                )
FROM    @Calls s INNER JOIN @Orders e   ON s.CallToId = e.CustomerId
Run Code Online (Sandbox Code Playgroud)

编辑: 现在我正在使用此查询

UPDATE c set OrderID = o1.OrderID
from @Calls c inner join @Orders o1 on c.CallTime > o1.OrderDate  
    left join @Orders o2 on c.CallTime > o2.OrderDate 
            and o2.OrderDate > o1.OrderDate 
where o2.OrderID is null
and o1.CustomerId = c.CallToId
Run Code Online (Sandbox Code Playgroud)

我的查询结果不是我需要的.

要求:如您所见,有两个订单.一个打开2010-12-12,一个打开2010-12-22.我想更新@Calls与CallTime相关的OrderId表.

简而言之,如果添加了后续订单,并且还有其他呼叫,那么我们假设新呼叫与最近的订单相关联

注意: 这是示例数据,因此我不总是有两个订单.可能有10多个订单和100多个电话以及1000个客户.

注2 我找不到这个问题的好标题.如果你想的更好,请改变它.

Edit2: 答案中提供的查询花费了太多时间.要更新的记录总数约为250000.

谢谢.

Dam*_*ver 5

您可以使用左连接来检查"不良"行,并在WHERE子句中将其删除:

UPDATE c set OrderID = o1.OrderID
from @Calls c
    inner join
        @Orders o1
            on
                c.CallTime > o1.OrderDate
            left join
        @Orders o2
            on
                c.CallTime > o2.OrderDate and
                o2.OrderDate > o1.OrderDate
where
    o2.OrderID is null

select * from @Calls
Run Code Online (Sandbox Code Playgroud)

即,您首先使用常规联接设置查询 - 您希望在调用(c)之前找到订单(o1)中的行.但是这可以返回多行(如果在调用(c)之前发生多个行.那么你执行另一个连接到命令(o2)寻找在(o1)中找到的行之后但仍然在调用之前的行(c)如果我们可以进行这样的匹配,那么我们毕竟不需要那个(o1)行.所以我们在WHERE子句中过滤掉那个组合的行.


现在您已经添加了CustomerID,您还需要考虑在您的连接条件中 - 左连接到@Orders(o2)将会找到任何以后的调用,而不仅仅是同一个客户的调用.要保持两个连接之间的对称顺序:

UPDATE c set OrderID = o1.OrderID
from @Calls c
    inner join
        @Orders o1
            on
                c.CallTime > o1.OrderDate
                            and c.CallToID = o1.CustomerId /* <-- New line 1 */
            left join
        @Orders o2
            on
                c.CallTime > o2.OrderDate and
                o2.OrderDate > o1.OrderDate
                            and c.CallToID = o2.CustomerId /* <-- New line 2 */
where
    o2.OrderID is null
Run Code Online (Sandbox Code Playgroud)

这也有望解决一些性能问题.