我经常遇到这样的查询:
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.A
,a.B
和a.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
之外)都看起来很简单
有什么想法吗?
答案 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)中实现:
这些改进通常已经在MySQL中使用了3.5年(在我撰写本文时是2019年4月,而5.7在2015年10月正式发布)。 MySQL会因这项改进而获得认可需要多少年?
P.S。我不知道其他哪些RDBMS产品可以正确进行功能依赖性分析。