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

时间:2011-01-01 16:49:01

标签: 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。我想更新@Calls表,其中包含与CallTime相关的OrderId。

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

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

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

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

感谢。

1 个答案:

答案 0 :(得分: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

即。首先使用常规连接设置查询 - 您希望在调用(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

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