T-SQL 中多个字段中的相同 window 函数:优化?

Same window function in multiple fields in T-SQL: optimization?

我在 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]

它是被评估了两次还是查询优化器第二次重用了结果? 提前致谢

我们可以像下面这样重写查询,以便只使用一次该函数吗?

       -- 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]; 

您可以通过查看 实际 执行计划(例如 运行 使用 "include actual execution plan" 开启)。

如果您有一个支持 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;