SQL 服务器中的游标:高性能惩罚
Cursors in SQL Server: High Performance Penalty
调用表
CallId | Date | Time | MemberID | CallDuration
12 | 02.02.2015| 13:33:54| 3245| 234 |
13 | 02.02.2015| 13:37:24| 3245| 33 |
ActivityTable
Date*********** Time*** MemberId ***Activity
02.02.2015*** 13:31:22*** 3245*** A
02.02.2015*** 13:34:54*** 3245*** B
我的老板想知道 activity 成员(员工)在收到 phone 打电话。此数据存储在上面给出的两个表中。
我正在使用 Cursor 从 CallTable 获取每一行,然后在循环中使用另一个游标来检索最后一个 activity。 CallTable 中大约有 100 万行,处理它需要很长时间。 CallTable中没有基于CallID的Primary-Foreign key关系。
这里有人可以建议我如何通过 JOIN 实现此目的并避免使用游标吗?
提前致谢
在 SQL 服务器中,您可以使用相关子查询或使用 APPLY
来执行此操作。例如:
select c.*, a.activity
from calltable c outer apply
(select top 1 a.*
from activitytable a
where a.memberId = c.memberId and
a.datetime <= c.datetime
order by a.datetime asc
) a;
这假定 date/time 在同一列中,这将是存储此值的正确方法。如果它们在不同的列中,则类似(但更复杂)的逻辑有效。
为了性能,您需要 activitytable(memberid, datetime)
上的索引。
滞后和超前可能对您有所帮助
这是一个 Northwind 迷你示例。
select * , DaysSincePreviousOrder = datediff(d , PreviousValue, OrderDate) from (
SELECT
LAG(p.OrderDate) OVER (ORDER BY p.CustomerID, p.OrderDate) PreviousValue,
p.CustomerID, p.OrderDate,
LEAD(p.OrderDate) OVER (ORDER BY p.CustomerID , p.OrderDate) NextValue
FROM dbo.Orders p where CustomerID = 'anatr' ) as derived1
where derived1.OrderDate = (select max(OrderDate) from dbo.Orders o where o.CustomerID = derived1.CustomerID)
GO
99.9% 的事情都可以在没有光标的情况下完成。我在 10 年内编写了一个游标......并且没有重新访问它以查看是否可以重构。
我认为这是 "hit several database" 类型的事情。但这是规则的微小例外......继续追求 "set based" 和非游标解决方案。
调用表
CallId | Date | Time | MemberID | CallDuration
12 | 02.02.2015| 13:33:54| 3245| 234 |
13 | 02.02.2015| 13:37:24| 3245| 33 |
ActivityTable
Date*********** Time*** MemberId ***Activity
02.02.2015*** 13:31:22*** 3245*** A
02.02.2015*** 13:34:54*** 3245*** B
我的老板想知道 activity 成员(员工)在收到 phone 打电话。此数据存储在上面给出的两个表中。
我正在使用 Cursor 从 CallTable 获取每一行,然后在循环中使用另一个游标来检索最后一个 activity。 CallTable 中大约有 100 万行,处理它需要很长时间。 CallTable中没有基于CallID的Primary-Foreign key关系。
这里有人可以建议我如何通过 JOIN 实现此目的并避免使用游标吗?
提前致谢
在 SQL 服务器中,您可以使用相关子查询或使用 APPLY
来执行此操作。例如:
select c.*, a.activity
from calltable c outer apply
(select top 1 a.*
from activitytable a
where a.memberId = c.memberId and
a.datetime <= c.datetime
order by a.datetime asc
) a;
这假定 date/time 在同一列中,这将是存储此值的正确方法。如果它们在不同的列中,则类似(但更复杂)的逻辑有效。
为了性能,您需要 activitytable(memberid, datetime)
上的索引。
滞后和超前可能对您有所帮助
这是一个 Northwind 迷你示例。
select * , DaysSincePreviousOrder = datediff(d , PreviousValue, OrderDate) from (
SELECT
LAG(p.OrderDate) OVER (ORDER BY p.CustomerID, p.OrderDate) PreviousValue,
p.CustomerID, p.OrderDate,
LEAD(p.OrderDate) OVER (ORDER BY p.CustomerID , p.OrderDate) NextValue
FROM dbo.Orders p where CustomerID = 'anatr' ) as derived1
where derived1.OrderDate = (select max(OrderDate) from dbo.Orders o where o.CustomerID = derived1.CustomerID)
GO
99.9% 的事情都可以在没有光标的情况下完成。我在 10 年内编写了一个游标......并且没有重新访问它以查看是否可以重构。 我认为这是 "hit several database" 类型的事情。但这是规则的微小例外......继续追求 "set based" 和非游标解决方案。