为什么MySQL允许没有聚合功能的“分组”查询?


67

惊喜-这是MySQL中完全有效的查询:

select X, Y from someTable group by X

如果您在Oracle或SQL Server中尝试了此查询,则会收到自然错误消息:

Column 'Y' is invalid in the select list because it is not contained in 
either an aggregate function or the GROUP BY clause.

那么MySQL如何确定每个X显示哪个Y?它只是选择一个。据我所知,它只是选择找到的第一个Y。理由是,如果Y既不是聚合函数也不在group by子句中,则在查询中指定“选择Y”是没有道理的。因此,作为数据库引擎的我将返回我想要的任何东西,而您会喜欢它。

甚至还有一个MySQL配置参数来关闭这种“松散性”。 http://dev.mysql.com/doc/refman/5.7/zh-CN/sql-mode.html#sqlmode_only_full_group_by

这篇文章甚至提到MySQL在这方面被批评为不符合ANSI-SQL。 http://www.oreillynet.com/databases/blog/2007/05/debunking_group_by_myths.html

我的问题是: 为什么MySQL以这种方式设计?他们打破ANSI-SQL的理由是什么?


让我这样说吧。我认为这种设计选择等同于选择允许和忽略(例如,让“ null”为左侧值)的编程语言。例如“ null = 3”。没有任何理由让它发生。这种错误始终是危险的错误。
亚伦·

3
@lumpynose,废话,这也许是真实的预5.x的
约翰·

4
@lumpynose您可以参考一下您的确认书吗?
Barranka

哦,伙计,我一直想要类似的东西,在SQL Server中是如此。我一直把它想象成“我想要“ PICKANY(col)”聚合函数,甚至更好的是“ UNIQUEVALUE(col)”函数,如果结果在整个组中不是恒定的,那将出错。使用“ MAX(col)”作为我的“所有值都相同,给我那个值”聚合函数
Brondahl

抱歉,以上评论是一个粗鲁的说法:“ IMO,答案是因为此功能确实有用。但是除了'因为我想要它'之外,我没有其他引用。”
布隆达尔

Answers:


23

我认为这是要处理按一个字段分组意味着其他字段也要分组的情况:

SELECT user.id, user.name, COUNT(post.*) AS posts 
FROM user 
  LEFT OUTER JOIN post ON post.owner_id=user.id 
GROUP BY user.id

在这种情况下,user.name对于每个user.id始终是唯一的,因此在GROUP BY子句中不需要使用user.name是很方便的(尽管正如您所说,存在一定的问题范围)


1
因此,这仅仅是为了节省一些打字(如键盘打字)?嘿。
亚伦·

2
GROUP BY子句中的列较少意味着更快的执行时间,因此是一种优化的技巧。我一直MAX(user.name) AS name在ANSI SQL实现中的类似查询中使用。
09年

@wqw:垃圾。user.name属于组或处于合计中。这充其量是模棱两可的。请参阅对stackoverflow.com/questions/6060241/…的评论。只有MySQL允许这种bollock,例如stackoverflow.com/q/6642241/27535
gbn

6
@gbn:垃圾是什么?SELECT user.id, ANY(user.name) .. GROUP BY user.id如果user.id上存在PK,则使用不确定的东西并不是不确定的。显然,从中选择的存储桶user.name会包含一个唯一值-对其ID进行分组的用户的名称。使用MAX(user.name) AS name或任何其他方式充其量都是很尴尬的(尽管意图更明确地记录在案)。可能是“这取决于”再次..的情况下
wqw

2
AFAIK最新的SQL标准要求所有选择的字段都必须是组字段或聚合字段,或要为该组确定的字段,例如上例中的user.name。因此,MySQL符合最新的标准,因为它们允许您选择既不是组字段也不是聚合字段,而是为组确定的字段,这是许多其他dbms不允许的。但是,MySQL通过不检查字段是否真正确定来破坏标准合规性,因此也允许不确定的字段。[待续]
Thorsten Kettner 2014年

24

根据此页面(5.0在线手册),它具有更好的性能和用户便利性。


+1,代表MySQL的答案:)。我一直在使用它来避免执行子查询ORDER BY ... LIMIT 1……您只需要小心一点,即知道在非聚合列中收到的数据对于符合您条件的所有行都是随机的。
凯文·尼尔森2014年

LInk已死,您可以更新它吗?或最好还是在此处插入文档,以防万一它再次破裂
szx

3

不幸的是,几乎所有的SQL变体都有可能破坏ANSI并导致无法预测的结果。

在我看来,他们希望将其像许多其他系统所具有的“ FIRST(Y)”功能一样对待。

MySQL团队很可能对此构造感到遗憾,但由于可能破坏的应用程序数量,因此不想停止支持。


看来确实是这样。
僵局

1

当您使用不带聚集函数的GROUP BY时,MySQL将此视为单列DISTINCT。使用其他选项,您要么使整个结果都与众不同,要么必须使用子查询等。问题是结果是否真正可预测。

另外,这个线程中有很好的信息。



-1

它实际上是一个非常有用的工具,当您按字段分组时,所有其他字段都不必包含在聚合函数中。您可以对结果进行操作,只需先对其进行排序,然后再对其进行分组即可。例如,如果我想获取用户登录信息,并且想查看用户上次登录的时间,我会这样做。

桌子

USER
user_id | name

USER_LOGIN_HISTORY 
user_id | date_logged_in

USER_LOGIN_HISTORY对于一个用户有多行,因此,如果我加入用户,它将返回很多行。因为我只对最后一个条目感兴趣,所以我会这样做

select 
  user_id,
  name,
  date_logged_in

from(

  select 
    u.user_id, 
    u.name, 
    ulh.date_logged_in

  from users as u

    join user_login_history as ulh
      on u.user_id = ulh.user_id

  where u.user_id = 1234

  order by ulh.date_logged_in desc 

)as table1

group by user_id

这将返回一行,其中包含用户名和该用户上次登录的时间。


我上面的示例仅用于说明如何处理返回的结果。我并不是说这就是您将以最简单的方式返回该信息的方式。您将使用MAX函数。对于更复杂的查询,能够进行分组而不将聚合函数用于所有其他字段变得非常有用
Nick Dennies

这个人为设计的例子比简单地做一个“ max”要长而且慢,因此完全不支持您的说法,它根本不是“非常有用的工具”。如果您甚至无法构想它有用的示例,那么我会严重质疑它的有用性。我也不相信故意使用经常不确定的功能在复杂的查询中会变得更加有用。
蒂姆·高铁耶
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.