SELECT 和 GROUP BY 子句中的 SQL 计算字段

Dr.*_*rew 12 sql-server

通常在查询我的 MS SQL Server 数据库时,我需要创建一个计算字段,例如

(CASE WHEN A.type = 'Workover' THEN 'Workover' 
      ELSE (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' 
                 WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' 
                 WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' 
                 ELSE 'Other' 
            END)
END)
Run Code Online (Sandbox Code Playgroud)

然后我需要通过这个计算字段(等等)对我的结果进行分组。因此,我在 SELECT 和 GROUP BY 子句中进行了相同的计算。SQL 服务器实际上执行了两次这些计算,还是它足够聪明,只执行一次?

Pau*_*ite 15

我在 SELECT 和 GROUP BY 子句中进行了相同的计算。SQL 服务器实际上执行了两次这些计算,还是它足够聪明,只执行一次?

简单的答案是,SQL Server 没有对标量表达式在执行时计算的时间和次数做出一般性保证。

优化器和执行引擎中有各种复杂的(和未记录的)行为,涉及标量表达式的放置、执行和缓存。Books Online对此没有太多可说的,但它确实说的是:

计算标量描述

这描述了我之前提到的行为之一,延迟执行表达式。我在这篇博文中写了一些其他当前行为(可能随时改变)。

另一个考虑因素是查询优化使用的成本模型目前在标量表达式的成本估计方面做的不多。如果没有强大的成本计算框架,当前的结果是基于广泛的启发式或纯粹的机会。

对于非常简单的表达式,在大多数情况下,表达式计算一次还是多次可能没有太大区别。也就是说,我遇到过大型查询,当表达式被冗余评估非常多次时,性能受到不利影响,或者评估发生在单个线程上,在执行的并行分支中进行评估是有利的计划。

总之,当前的行为是未定义的,执行计划中没有什么可以帮助您弄清楚发生了什么(并且附加调试器来检查详细的引擎行为并不总是很方便,如博客文章中所述)。

如果您遇到标量评估问题对性能很重要的情况,请向 Microsoft 支持提出问题。这是提供反馈以改进产品未来版本的最佳方式。


小智 2

性能只是一方面。另一个是可维护性。

就我个人而言,我倾向于执行以下操作:

SELECT T.GroupingKey, SUM(T.value)
FROM
(
    SELECT 
        A.*
        (CASE WHEN A.type = 'Workover' THEN 'Workover' ELSE 
        (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' ELSE 'Other' END)
        END) AS GroupingKey
    FROM Table AS A
) AS T

GROUP BY T.GroupingKey
Run Code Online (Sandbox Code Playgroud)

更新:

如果您不喜欢嵌套,您可以为每个需要使用复杂表达式的表创建 VIEW。

CREATE VIEW TableExtended
AS 
SELECT 
    A.*
    (CASE WHEN A.type = 'Workover' THEN 'Workover' ELSE 
    (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' ELSE 'Other' END)
    END) AS GroupingKey
FROM Table AS A
Run Code Online (Sandbox Code Playgroud)

然后你就可以进行 select 而无需进行额外的嵌套;

SELECT GroupingKey, SUM(value)
FROM TableExtended
GROUP BY GroupingKey
Run Code Online (Sandbox Code Playgroud)