为什么MySQL添加一个与SQL标准冲突的function?

我习惯于Microsoft技术,包括SQL Server。 今天我遇到了一个问题解答,其中引用了MySQL文档中的以下内容:

标准SQL将拒绝您的查询,因为您不能select聚合查询中不属于GROUP BY子句一部分的非聚合字段。 MySQL扩展了GROUP BY的使用,以便select列表可以引用未在GROUP BY子句中命名的非聚集列。 这意味着前面的查询在MySQL中是合法的。 通过避免不必要的列sorting和分组,您可以使用此function获得更好的性能。 但是,这非常有用,因为在GROUP BY中未命名的每个非聚合列中的所有值对于每个组都是相同的。 服务器可以自由select每个组的任何值,因此除非它们相同,否则所选的值是不确定的

这个MySQL扩展的原因是什么,如果它与SQL标准冲突?

标准SQL将拒绝您的查询,因为您不能select聚合查询中不属于GROUP BY子句一部分的 非聚合字段

这是正确的,直到1992年

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

从SQL-2003标准6IWD6-02-Foundation-2011-01.pdf,从http://www.wiscorp.com/ ,段落7.12(查询规范),页面398

17)如果T是一个分组表,则G是T的分组列。在((select列表))中包含的每个((值expression式))中,引用T列的每个列引用将引用一些列C在function上依赖于G 或者应该包含在聚集查询为QS((集合函数规范))的聚合参数


现在,MYSQL已经实现了这个function, 不仅允许在function上依赖于分组列而是允许所有列的列 。 这对于不了解分组是如何工作的用户造成了一些问题,并且在不期望的情况下得到了不确定的结果。

但是你说得对,MySQL已经添加了一个与SQL标准冲突的function(尽pipe你似乎认为这是错误的原因)。 这并不完全准确,因为他们已经添加了SQL标准function,但并不是最好的方式(更像简单的方法),但是它与最新的标准冲突。

为了回答你的问题,这个MySQLfunction(扩展)的原因是我想是符合最新的SQL标准(2003+)。 为什么他们select这样实施(不完全符合),我们只能推测。

正如@Quassnoi和@Johan用示例回答的,主要是性能和可维护性问题。 但是,不能轻易地将RDBMS更改为足够聪明(不包括Skynet)来识别function依赖列,所以MySQL开发人员做出了一个select:

我们(MySQL)给你(MySQL用户)这个function是SQL-2003的标准。 它提高了某些GROUP BY查询的速度,但有一个问题。 您必须小心(而不是SQL引擎),以便SELECTHAVING列表中的列在function上依赖于GROUP BY列。 如果不是,你可能会得到不确定的结果。

如果你想禁用它,你可以设置sql_modeONLY_FULL_GROUP_BY

这些都在MySQL文档中:对GROUP BY (5.5)的扩展 – 虽然不是在上面的措辞中,但在你的报价中(他们甚至忘记提及这是一个偏离标准SQL-2003,而不是标准的SQL-92)。 这种select在所有软件中都是常见的,包括其他的RDBMS。 它们是为了性能,向后兼容性和许多其他原因而制造的。 Oracle有着名的'' is the same as NULL ,SQL-Server也可能有一些。

还有Peter Bouman的这篇博客文章,MySQL开发人员的select在这里得到捍卫: 解读GROUP BY的神话 。


更新(2011)

正如@Mark Byers在一篇评论中(在DBA.SE的相关问题中)通知我们的那样, PostgreSQL 9.1添加了一个新function (发布date:2011年9月),用于此目的。 它比MySQL的实现更加严格,更接近标准。


更新2(2015)

MySQL宣布在5.7版本中,行为被改进以符合标准并且实际上识别函数依赖(甚至比Postgres实现更好)。 文档: MySQL处理GROUP BY (5.7)和Peter Bouman的另一篇博文: MySQL 5.7.5: GROUP BY尊重函数依赖关系!

这个MySQL扩展的原因是什么,如果它与SQL标准冲突?

它可以让你写这样的查询:

 SELECT a.*, COUNT(*) FROM a JOIN b ON ba = a.id GROUP BY a.id 

其他系统将要求您将a中的所有列添加到GROUP BY列表中,这会使查询更大,更less维护和效率更低。

在这种forms下(通过PK分组),这与标准并不矛盾,因为a中的每一列在function上都依赖于它的主键。

但是, MySQL并没有真正检查函数的依赖关系,而是让你select不依赖于分组集的function。 这可能会产生不确定的结果,不应该依赖。 唯一保证的是列值属于共享分组expression式的一些logging(甚至不是一个logging!)。

通过将sql_mode设置为sql_mode ,可以禁用此行为。

简短的回答
这是一个速度黑客

这是默认启用,但可以使用此设置禁用: http : //dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_only_full_group_by

很长的答案非标准的速记组的原因是它是一个速度的破解。
MySQL让程序员确定选定的字段是否在function上依赖于group by子句。
数据库不做任何testing,只是select它find的第一个结果作为字段的值。
这导致相当大的加速。

考虑这个代码:

 SELECT f1, f2, f3, f4 FROM t1 GROUP BY f2 -- invalid in most SQL flavors, valid in MySQL 

MySQL只会select它find的第一个值,花费最less的时间。
f1,f3,f4将来自同一行,但如果涉及多个带连接的表,则此关系将分解。

为了在SQL服务器上做同样的事情,你必须这样做

 SELECT MIN(f1), f2, MIN(f3), MIN(f4) FROM t1 GROUP BY f2 -- valid SQL, but really a hack 

数据库现在必须检查所有的结果,find最小值,吞噬和膨化。
f1,f3,f4很可能没有任何关系,不会来自同一行。

但是,如果你这样做:

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

所有其余的字段将在function上依赖于id
Rowcount将始终为1,并且f2count将为0(如果f2为null)或1。

在连接上,涉及大量的表,像1-n这样的configuration:

例:

网站1 – > n主题1 – > n主题1 – > npost1 – > 1人。

你做一个复杂的select涉及所有表,只是做一个GROUP BY posts.id
显然所有其他领域在function上依赖于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 //NEVER MIND THE SYNTAX ERROR WITH THE ALIASES 

给程序员的精神负担完全一样。

所有的大型数据库pipe理系统都有自己的口味和扩展。 否则为什么会有不止一个呢?

严格遵循SQL标准是非常好的,但提供更多function的扩展甚至更好 。 文档中的引用说明了这个function是如何有用的。

在这种情况下没有什么冲突,所以我真的不知道这个问题。