T-SQL 中多个字段中的相同窗口函数:优化?
Posted
技术标签:
【中文标题】T-SQL 中多个字段中的相同窗口函数:优化?【英文标题】:Same window function in multiple fields in T-SQL: optimization? 【发布时间】:2017-10-11 12:04:32 【问题描述】:我在 T-SQL SELECT 的多个 WHEN 中使用了相同的窗口函数。
SELECT
ModuleUsage = CASE
WHEN Operation = 1 and OperationResult=1 and 1 != LAG(operation) OVER(
PARTITION BY moduleid, clientname
ORDER BY moduleid, clientname, timestamp, operation
)
THEN 1
WHEN Operation = 2 and OperationResult=3 and 1 = LAG(operation) OVER(
PARTITION BY moduleid, clientname
ORDER BY moduleid, clientname, timestamp, operation
)
THEN -1
ELSE 0
END
, *
FROM [dbo].[LicencesTracing]
是计算两次还是查询优化器第二次重用结果? 提前致谢
【问题讨论】:
你可以自己测试一下。使用 `SET STATISTICS` 并通过一个窗口调用进行查询。然后通过两个电话进行完整的查询。你觉得有什么不同吗? 【参考方案1】:我们可以像下面这样重写查询,以便只使用一次函数吗?
-- window function <> 1
SELECT CASE WHEN LAG(operation) OVER(PARTITION BY moduleid, clientname ORDER BY moduleid, clientname, timestamp, operation) <> 1
THEN CASE WHEN Operation = 1 and OperationResult=1 THEN 1 ELSE 0 END
-- window function = 1
ELSE
CASE WHEN Operation = 2 and OperationResult=3 THEN -1 ELSE 0 END
END AS ModuleUsage
,*
FROM [dbo].[LicencesTracing];
而我使用IIF
会简化代码:
SELECT CASE WHEN LAG(operation) OVER(PARTITION BY moduleid, clientname ORDER BY moduleid, clientname, timestamp, operation) <> 1
THEN IIF(Operation = 1 and OperationResult=1, 1, 0)
ELSE IIF(Operation = 2 and OperationResult=3, -1, 0)
END AS ModuleUsage
,*
FROM [dbo].[LicencesTracing];
【讨论】:
如果 IIF 使用 2012+ ,则 IIF 将只是代码 ;) ...否则他们只会得到一个错误 @AlanBurstein 他们正在使用 2012+,因为他们正在使用LAG
函数 ;)
是的。呃。在进一步评论之前,我需要喝完咖啡。 ;)【参考方案2】:
您可以通过查看 实际 执行计划来确定您的 Window 函数是否被评估一次或多次(例如,在“包含实际执行计划”打开的情况下运行查询)。
如果您有一个索引来支持该窗口功能,那么它真的没关系,因为操作很便宜。 POC index 对此查询非常有帮助。索引如下所示:
CREATE nonclustered index xxx on dbo.LicencesTracing(moduleid, clientname, timestamp, operation)
INCLUDE <whatever columns you are returning>;
您可以简化查询和强制优化器通过在子查询中计算 Window 函数来评估它;像这样的东西: (请注意,我无法对此进行测试...)
SELECT ModuleUsage = CASE
WHEN Operation = 1 and OperationResult = 1 and lagOp <> 1 THEN 1
WHEN Operation = 2 and OperationResult = 3 and lagOp = 1 THEN -1 ELSE 0
END
FROM
(
SELECT Operation, OperationResult, lagOp =
LAG(operation) OVER(PARTITION BY moduleid, clientname
ORDER BY moduleid, clientname, timestamp, operation), *
FROM [dbo].[LicencesTracing]
) sub;
【讨论】:
谢谢你,Alan:你实际上给了我关于优化的非常有用的见解。我将@gotqn 的答案标记为答案,因为他从根本上解决了这个问题。以上是关于T-SQL 中多个字段中的相同窗口函数:优化?的主要内容,如果未能解决你的问题,请参考以下文章
Microsoft SQL Server 2022 新特性之 T-SQL 语言增强