有没有更清晰的方法来处理不在聚合函数或GROUP BY子句中的字段?

时间:2019-04-26 21:32:33

标签: mysql sql sql-server aggregation

我经常遇到这样的查询:

SELECT
    a.Id,
    a.A,
    a.B,
    a.C,
    SUM(b.Foo) AS foo
FROM
    TableA AS a
    JOIN TableB AS b
        ON a.Id = b.TableAId
GROUP BY a.Id;

在SQL Server中(如果ONLY_FULL_GROUP_BY为true,则为MySQL),此查询不好。一切都必须是a)聚合函数中的内容,或b)GROUP BY中的内容。

我的问题是,这两种解决方案看起来都很糟糕并且具有误导性。如果您选择MAX()之类的随机聚合函数,则会得到:

SELECT
    a.Id,
    MAX(a.A) AS A,
    MAX(a.B) AS B,
    MAX(a.C) AS C,
    SUM(b.Foo) AS foo
FROM
    TableA AS a
    JOIN TableB AS b
        ON a.Id = b.TableAId
GROUP BY a.Id;

此查询看起来像我们关心的a.Aa.Ba.C的最大值,并混淆了最大值毫无意义的事实。

GROUP BY好一点:

SELECT
    a.Id,
    a.A,
    a.B,
    a.C,
    SUM(b.Foo) AS foo
FROM
    TableA AS a
    JOIN TableB AS b
        ON a.Id = b.TableAId
GROUP BY a.Id, A, B, C;

,但仍然不是最佳选择。在具有复杂分组的大型查询中,拥有所有这些额外的字段将使其更难阅读,而我的最初印象是这里确实存在一些额外的分组层次结构。

我的背景主要是在MySQL中,而ONLY_FULL_GROUP_BY已关闭,因此我发现SQL Server中的此限制是不必要的。我希望两者之间能有一些快乐的媒介。对于计算机而言,查看此查询并查看TableA字段不需要聚合,而TableB中的任何字段(除TableAId之外)都看起来很简单

有什么想法吗?

2 个答案:

答案 0 :(得分:5)

这不是SQL Server问题!您看到的行为是MySQL异常(通常),这就是为什么现在默认设置是禁止该行为的原因。也就是说,SQL标准允许按表中的唯一列进行聚合并选择表中的其他列 。但是,我认为只有Postgres才能实现。

这是您遗漏的一种方法:

SELECT a.*, b.foo
FROM TableA a JOIN
     (SELECT b.TableAId, SUM(b.Foo) as foo
      FROM TableB b
      GROUP BY b.TableAId
     ) b
     ON a.Id = b.TableAId;

答案 1 :(得分:3)

MySQL 5.7和更高版本对此处理得很好:

mysql [localhost:5724] {msandbox} (test) > select @@sql_mode;
+-------------------------------------------------------------------------------------------------------------------------------------------+
| @@sql_mode                                                                                                                                |
+-------------------------------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql [localhost:5724] {msandbox} (test) > SELECT
    ->     a.Id,
    ->     a.A,
    ->     a.B,
    ->     a.C,
    ->     SUM(b.Foo) AS foo
    -> FROM
    ->     TableA AS a
    ->     JOIN TableB AS b
    ->         ON a.Id = b.TableAId
    -> GROUP BY a.Id;
Empty set (0.01 sec)

看,没有错误!

由于此查询是按TableA的唯一键进行分组的,因此它可以告诉TableA的其他列对要分组的列具有功能依赖性。因此,无需为模棱两可的结果产生错误。

选择列表中唯一不依赖分组列的功能的列是b.Foo,在此查询中该列安全地位于聚合函数中。

因此,虽然MySQL过去让开发人员知道如何编写避免歧义的查询,但现在有了两项改进,均在MySQL 5.7.5(2014-09-25)中实现:

  1. 默认情况下启用了ONLY_FULL_GROUP_BY
  2. MySQL分析功能依赖关系,如果未聚合的列在功能上依赖于分组列,则不会使开发人员感到沮丧。

这些改进通常已经在MySQL中使用了3.5年(在我撰写本文时是2019年4月,而5.7在2015年10月正式发布)。 MySQL会因这项改进而获得认可需要多少年?

P.S。我不知道其他哪些RDBMS产品可以正确进行功能依赖性分析。