GROUP BY 子句中 ISNULL() 的性能 SQL

Performance of ISNULL() in GROUP BY clause SQL

我最近一直在重构一些旧的查询,并注意到其中很多在 GROUP BY 子句中重复 ISNULL(),而在 SELECT 子句中使用它。我骨子里觉得删除 GROUP BY 子句中的 ISNULL() 会提高性能,但我找不到任何文档来说明它是否真的可能。这是我的意思:

SELECT
     ISNULL(Foo,-1) AS Foo
    ,ISNULL(Bar,-1) AS Bar
    ,SUM(This)      AS This
    ,SUM(That)      AS That
FROM
            dbo.ThisThatTable   AS ThisThat
LEFT JOIN   dbo.FooBarTable     AS FooBar   ON ThisThat.FooBarId = FooBar.Id
GROUP BY
     ISNULL(Foo,-1)
    ,ISNULL(Bar,-1);
GO

以上是我不断遇到的方式 - 当 Foo 列上有分组时,所选列的 SELECTGROUP BY 完全匹配。下面的示例是一个可能的替代方案 - 一些可能不必要的 ISNULL() 调用已被删除,并且 SELECTGROUP BY 子句不再匹配。

SELECT
     ISNULL(Foo,-1) AS Foo
    ,ISNULL(Bar,-1) AS Bar
    ,SUM(This)      AS This
    ,SUM(That)      AS That
FROM
            dbo.ThisThatTable   AS ThisThat
LEFT JOIN   dbo.FooBarTable     AS FooBar   ON ThisThat.FooBarId = FooBar.Id
GROUP BY
     Foo
    ,Bar;
GO

我想也许当 SELECT 和 GROUP BY 子句匹配时,优化器只需要执行一次 ISNULL() 计算就知道发生了什么,所以理论上可能是按实际选择的结果分组更有表现力?或者,也许最好避免添加根本不改变数据粒度的第二组 ISNULL() 调用......也许优化器足够聪明,可以意识到分组中的 NULLS 是(在这种情况下)选择中的 -1s...?

我个人更愿意删除任何不必要的功能,尤其是那些可能会影响索引使用的功能,但是当我在线查看时,对性能的引用都像答案here,关于在中使用ISNULL() WHERE 子句,我已经知道要避免。

我也怀疑任何收益都会微乎其微,所以这实际上是在寻求学术或理论答案,但在我工作的过程中,我一直在想,这让我很烦恼,所以我想我会问是否任何人都有任何想法。

Non-aggregated SELECT 子句中的列通常必须精确匹配 GROUP BY 子句中的列。如果我是你,并且我正在处理经过测试的生产代码,我不会进行你建议的更改。

编辑 non-aggregated SELECT 列和 GROUP BY 列之间的匹配对于 GROUP BY 是必需的。如果 SELECT 中的列 1:1 依赖于 GROUP BY 中的列,它将起作用。否则结果不明确。

在内部,SQL 并不是每个 ISNULL 都有两个副本。它们都在编译期间使用的内部树中一起展平。因此,在 SQL 服务器中考虑这种优化级别没有用。一个没有任何 ISNULL 的查询可能会执行得更快一些,并且可能会更快,具体取决于模式和查询的其余部分。但是,select 列表中的 ISNULL 和 GROUP BY 列表在大多数情况下不会在 SQL 中执行两次 - 这种详细程度可以显示在 showplan 中,但通常低于详细程度人们会关心检查。

这里有几个不同的方面需要考虑:

  • 在同一范围内多次引用同一值
    在大多数情况下,优化器足够聪明,可以将它们折叠成计算一次。事实上,你对它们的 GROUP BY 使这更有可能。

  • 在保证值不为null的情况下分组是否更快?
    可能吧,虽然我怀疑这种差异是可以衡量的。

  • SELECT不必完全匹配,只需要功能依赖GROUP BY列和聚合函数上即可.它在功能上可能不依赖于任何其他列。

  • 首要考虑的最重要的事情:索引。
    比其他考虑因素 重要得多。分组的时候,如果你能命中一个索引,那么速度会快很多,因为它可以去掉排序,只用Stream Aggregate。如果您在 GROUP BY 中使用 ISNULL(除非计算列或索引视图),这是不可能的。

请注意,您的结果不会相同:第一个示例将 NULL 组折叠到 -1 组中。第二个示例没有,因此您可能还想从 SELECT 中删除 ISNULL,以区分它们。或者,改用 WHERE ... IS NOT NULL