MySQL是否违反标准,允许选择不属于GROUP BY子句的列?

9 浏览
0 Comments

MySQL是否违反标准,允许选择不属于GROUP BY子句的列?

我习惯使用包括SQL Server在内的微软技术。今天我遇到了一个问答,其中引用了MySQL文档中的以下段落:

标准SQL会拒绝你的查询,因为在聚合查询中,你不能选择那些不在GROUP BY子句中的非聚合字段。MySQL扩展了GROUP BY的使用,以使选择列表可以引用不在GROUP BY子句中命名的非聚合列。这意味着在MySQL中,前面的查询是合法的。你可以利用这个特性来通过避免不必要的列排序和分组来获得更好的性能。然而,这主要在每个分组中的每个非聚合列的所有值都相同时才有用。服务器可以自由选择每个分组中的任何值,因此除非它们相同,否则所选择的值是不确定的。

MySQL是否通过允许这种情况来违反了标准?它是如何做到的?允许这样做的结果是什么?

0
0 Comments

MySQL是否通过允许选择不在group by子句中的列来违反了标准?

短答案:这是一种速度优化的方式,默认情况下开启,但可以通过设置进行关闭。

长答案:非标准的简写group by子句的原因是为了提高速度。MySQL允许程序员确定选择的字段是否与group by子句有函数依赖关系。数据库不进行任何测试,只是选择找到的第一个结果作为字段的值。这会带来显著的速度提升。

考虑以下代码:

SELECT f1, f2, f3, f4 FROM t1 GROUP BY f2  -- 在大多数SQL语言中无效,在MySQL中有效

MySQL将只选择它找到的第一个值,花费最少的时间。f1、f3和f4将来自同一行,但如果涉及多个表和连接,则这种关系将分崩离析。

要在SQL Server中实现类似的功能,您需要执行以下操作:

SELECT MIN(f1), f2, MIN(f3), MIN(f4) FROM t1 GROUP BY f2  -- 有效的SQL,但实际上是一种技巧

数据库现在必须检查所有结果以找到最小值,这会花费更多的时间。f1、f3和f4很可能没有关联,也不会来自同一行。

但是,如果您执行以下操作:

SELECT id as `primary_key`, count(*) as rowcount, count(f2) as f2count, f2, f3, f4 FROM t1 GROUP BY id

其余所有字段都将与id有函数依赖关系。rowcount始终为1,f2count将为0(如果f2为null)或1。

在涉及大量表和连接的情况下,以1-n配置为例:

例如:网站1 -> n主题1 -> n线程1 -> n帖子1 -> 1人。

如果您执行涉及所有表的复杂查询,并只对GROUP BY posts.id,显然所有其他字段都与posts.id有函数依赖关系(仅与posts.id有关)。因此,在group by子句中列出更多字段或强制使用聚合函数都没有意义。为了加快速度,MySQL不强制您这样做。

但是,您需要理解函数依赖的概念以及表中的关系和您编写的连接,这对程序员来说是一种负担。

然而,使用以下方式:

SELECT 
  posts.id, MIN(posts.f2)
  ,MIN(threads.id), min(threads.other)
  ,MIN(topics.id), ....
  ,MIN(website.id), .....
  ,MIN(Person.id), ...
FROM posts p
INNER JOIN threads t on (p.thread_id = t.id)
INNER JOIN topic to on (t.topic_id = to.id)
INNER JOIN website w ON (w.id = to.website_id)
INNER JOIN person pe ON (pe.id = p.person_id)
GROUP BY posts.id   //不用理会别名的语法错误

对程序员来说,负担是完全相同的。

0
0 Comments

MySQL是否通过允许选择不在GROUP BY子句中的列来违反了标准?为什么会出现这种情况?有什么解决方法?

MySQL是否通过允许选择不在GROUP BY子句中的列来违反了标准?为什么会出现这种情况?

是的,MySQL通过允许在GROUP BY子句中选择不包含的列违反了标准。这种情况出现的原因是为了简化查询,提高效率和可维护性。其他系统要求在GROUP BY列表中添加所有的列,这使得查询变得更大、更难维护和效率更低。

解决方法:

虽然MySQL允许在GROUP BY子句中选择不包含的列,但它并不真正检查这些列是否与分组集合存在函数依赖关系。这可能导致不确定的结果,因此不应依赖于这种行为。唯一可以保证的是列值属于共享分组表达式的某些记录(甚至不是一条记录!)。

为了避免这种问题,可以通过将sql_mode设置为ONLY_FULL_GROUP_BY来禁用此行为。

在Postgres 9.1+中,如果a_id是主键,允许使用此语法(根据SQL标准),这是有意义的。然而,MySQL无论如何都允许这样做,这是可疑的行为。

0
0 Comments

MySQL是否通过允许选择不在GROUP BY子句中的列而打破了标准?

从1992年开始,标准SQL将拒绝这种查询,因为您不能选择不在GROUP BY子句中的非聚合字段。

但是从2003年开始,这是错误的。

根据SQL-2003标准,第7.12段(查询规范),页面398:

如果T是一个分组表,那么让G成为T的分组列集合。在选择列表中的每个((值表达式))中,引用T的列的每个列引用都应该引用在G上功能依赖的某个列C或者应该包含在聚合查询是QS的聚合参数的((集函数规范))中。

现在,MySQL通过允许不仅是功能依赖于分组列的列,而且允许所有列来实现了这个功能。这给那些不理解分组工作原理并且在他们不期望的地方得到不确定结果的用户带来了一些问题。

但是您说MySQL添加了一个与SQL标准冲突的功能(扩展),这是正确的(尽管您似乎认为原因是错误的)。它并不完全准确,因为他们添加了一个符合SQL-2003标准的功能,但不是以最佳方式(更像是简单方式),但它确实与最新的标准冲突。

回答您的问题,这个MySQL功能(扩展)的原因,我想是为了与最新的SQL标准(2003+)一致。为什么他们选择以这种方式实现(不完全符合),我们只能推测。

正如和回答示例,这主要是性能和可维护性问题。但是不能轻易改变RDBMS以使其能够识别功能依赖列,所以MySQL开发人员做出了选择:

我们(MySQL)为您(MySQL用户)提供了这个在SQL-2003标准中的功能。它提高了某些GROUP BY查询的速度,但需要小心(而不是SQL引擎),因此SELECT和HAVING列表中的列在功能上依赖于GROUP BY列。否则,您可能会得到不确定的结果。

如果您想禁用它,可以将sql_mode设置为ONLY_FULL_GROUP_BY。

这些都在MySQL文档中:GROUP BY扩展(5.5)-虽然不是上述措辞,但与您的引用相同(他们甚至忘记提到这是与标准SQL-2003的偏离)。我认为这种选择在所有软件中都很常见,包括其他RDBMS。它们是为了性能,向后兼容性和其他很多原因而做出的选择。Oracle有著名的''与NULL相同,SQL Server也可能有一些。

还有这篇由Peter Bouman撰写的博文,其中为MySQL开发人员的选择进行了辩护:Debunking GROUP BY myths。

在2011年,正如Byers在DBA.SE的一个相关问题的评论中告诉我们的那样,PostgreSQL 9.1添加了一个新功能(发布日期:2011年9月),旨在解决这个问题。它比MySQL的实现更严格,更接近标准。

后来,在2015年,MySQL宣布在5.7版本中,行为改进以符合标准,并实际识别功能依赖关系,(甚至比Postgres的实现更好)。文档:MySQL Handling of GROUP BY (5.7)和Peter Bouman的另一篇博文:MySQL 5.7.5: GROUP BY respects functional dependencies!

还有一个关于SQL Server的信息,NULL可以违反唯一约束。

感谢Byers的评论,我在写答案时找不到这个信息。在msdn网站上,他们提到:与参与UNIQUE约束的任何值一样,每个列只允许一个NULL值。我们可以得出结论,NULL就像任何其他值一样...

是的,这显然是标准的违规。4.6.5.4说:唯一约束仅在表中没有两行具有唯一列中相同的非NULL值时满足。

对于历史学家来说,三年前的Postgres提交,我认为它仍然是唯一完全符合标准的实现。

“与最新的SQL标准一致”-我非常怀疑。MySQL的“宽松”GROUP BY处理在SQL:2003标准之前就已经存在于产品中。它更多地遵循了MySQL的指南:“不要抛出错误,而是更喜欢返回不确定的结果”

_horse_with_no_name 我认为你是对的。我后来通过添加下一段来编辑问题,同意这可能是与性能有关的。我还不确定这个添加是在2003年还是在1999年的标准中。

您能详细说明为什么MySQL对"功能依赖关系"检测的实现甚至比Postgres中的更好吗?

_horse_with_no_name 这么久以来我写这篇文章。我想我是说Postgres仅在检查FD时考虑主键约束。如果我没记错的话,MySQL还检查唯一约束。不确定它是否也通过外键来检查“级联”依赖关系,需要检查一下。所以,我想我应该用“实施了更多的FD检查”这样的措辞来表达,你觉得呢?

0