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


11

通常在查询我的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)

然后我需要按此计算字段(除其他外)对结果分组。因此,我在SELECT和GROUP BY子句中都进行了相同的计算。SQL Server是实际上执行两次这些计算,还是仅执行一次就足够聪明?

Answers:


13

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

简单的答案是,SQL Server不对执行时将评估标量表达式的时间以及次数进行一般保证。

在优化器和执行引擎中,关于标量表达式的放置,执行和缓存,存在各种复杂(且未记录)的行为。联机丛书对此无话可说,但是它的确是这样的:

计算标量描述

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

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

对于非常简单的表达式,在大多数情况下,对表达式进行一次还是多次评估可能并没有多大区别。就是说,我遇到了很多查询,当对表达式进行大量重复计算时,性能会受到不利影响;或者在单个线程中进行计算,而在执行的并行分支中进行计算将是有利的。计划。

总而言之,当前行为是不确定的,执行计划中没有太多内容可以帮助您弄清发生了什么(并且像博客文章中那样,连接调试器来检查详细的引擎行为并不总是很方便)。

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


3

正如您对问题的评论所言,答案(至少以我的经验)是“是”。SQL Server通常足够聪明,可以避免重新计算。您可以通过在SQL Server Management Studio中显示执行计划来验证这一点。每个计算的字段都被指定Exprxxxxx(其中xxxxx是一个数字)。如果您知道要查找的内容,则应该能够验证它使用了相同的表达式。

要增加讨论范围,您的其他美学选择是常用的表格表达式

with [cte] as
(
    select
        (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 [group_key],
         *
    from
        [some_table]
)
select
    [group_key],
    count(*) as [count]
from
    [cte]
group by
    [group_key]

简短的回答,它们在功能上与视图相同,但仅在下一个语句中有效。我将它们视为派生表的一种更具可读性的替代方法,因为它避免了嵌套。

尽管与该问题无关,但是它们可以引用自己,并以此方式构造递归查询。


@Quick Joe Smith:我认为您对Exprxxxxx是正确的,因为我也已经看到了。但是,如果我为表达式手动指定一个名称(case ... end)作为OpType,然后在GROUP BY子句中使用字段OpType,则会收到错误消息,它是无效的列名。
Dr. Drew

不幸的是,通常两次指定表达式的唯一方法是使用上述方法之一:CTE,视图或嵌套查询。
快速乔·史密斯

2
除非您也了解CROSS APPLY
Andriy M

cross apply在这种情况下使用会有点麻烦,并且很可能通过引入不必要的自连接而损害性能。
快速乔·史密斯

2
我认为您不会“理解”这个建议。在CROSS APPLY刚刚定义了从同一行中列的别名。无需加入。例如SELECT COUNT(*), hilo FROM master..spt_values CROSS APPLY (VALUES(high + low)) V(hilo) GROUP BY hilo
马丁·史密斯

1

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

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

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

更新:

如果您不喜欢嵌套,则可以为每个需要使用复杂表达式的表创建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

然后,您可以选择而无需进行额外的嵌套;

SELECT GroupingKey, SUM(value)
FROM TableExtended
GROUP BY GroupingKey
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.