sql - 不使用游标并按日期更新表

标签 sql sql-server sql-server-2005 tsql 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

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

alt text

我正在使用以下查询(答案之前的旧查询)

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

编辑: 现在我正在使用这个查询

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

并且我的查询结果不是我需要的。

要求: 如您所见,有两个订单。一份是在2010-12-12,另一份是在2010-12-22。我想使用与 CallTime 相关的 OrderId 更新 @Calls 表。

简而言之,如果添加了后续订单,并且有进一步的调用,那么我们假设新的调用与最近的订单相关联

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

注释2 我找不到这个问题的好标题。如果您想到更好的请更改它。

编辑2: 答案中提供的查询花费了太多时间。要更新的记录总数约为 250000 条。

谢谢。

最佳答案

您可以使用左联接来检查“不需要的”行,并在 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

即您首先使用普通联接设置查询 - 您想要查找在调用 (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

这也有望解决一些性能困难。

关于sql - 不使用游标并按日期更新表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4574771/

相关文章:

sql - 如何计算表格中的持续时间

sql - Impala SQL : Merging rows with overlapping dates. WHERE EXISTS 不支持递归 CTE

database - Hibernate 和 Sql Server 最佳实践

sql-server - CLR存储过程: how to set the schema/owner?

sql-server-2005 - 在 SSRS 报告中显示 Tiff 文件

sql - 反对向其他表插入数据的SQL触发器的原因?

sql - Timescale run_job() 找不到定义的函数/过程

c# - 根据文本文件上的列表更快地从 SQL Server 中选择元素

C# 错误 : Index was outside the bounds of the array

sql-server - 在实时数据库中删除超过 17 亿行的未索引表(SQL 管理员噩梦)