为什么Mysql的Group By和Oracle的Group按行为不同
我多次发现Mysql的groupBy功能和Oracle的GroupBy功能表现不同
我多次在Oracle中发现错误(这实际上是错误的查询)但是Mysql会给这个结果
所以这个Mysql奇怪的行为背后有任何原因
答案 0 :(得分:11)
MySQL设计人员对GROUP BY
进行非标准扩展,试图使开发更容易,某些查询更有效。
这是他们的理由。
https://dev.mysql.com/doc/refman/8.0/en/group-by-handling.html
有一种名为ONLY_FULL_GROUP_BY
的服务器模式可以禁用非标准扩展。您可以使用此语句设置此模式。
SET SESSION SQL_MODE='ONLY_FULL_GROUP_BY'
以下是该页面的引用,重点补充。
如果禁用
ONLY_FULL_GROUP_BY
,则标准SQL使用GROUP BY
的MySQL扩展允许选择列表,HAVING
条件或ORDER BY
列表引用非聚合列即使列在功能上不依赖于GROUP BY
列...在这种情况下,服务器可以自由选择每个组中的任何值,因此除非它们相同,否则值选择不确定,这可能不是你想要的。
这里的重要词是不确定。这是什么意思?这意味着随机,但更糟。如果服务器选择了随机值,这意味着它会在不同的查询中返回不同的值,因此您在测试软件时有机会发现问题。但是在此上下文中 nondeterministic 意味着服务器每次都选择相同的值,直到它没有。
为什么它会改变它选择的价值?服务器升级是一个原因。表大小的更改可能是另一个。关键是,服务器可以随意返回它想要的任何值。
我希望新学习SQL的人能设置此ONLY_FULL_GROUP_BY
模式;他们从查询中获得更可预测的结果,服务器会拒绝非确定性查询。
答案 1 :(得分:8)
Oracle不扩展旧的SQL标准,该标准声明选择列表中未包含在聚合函数中的所有项都必须包含在group by子句中。
在标准SQL中,包含GROUP BY子句的查询不能引用选择列表中未在GROUP BY子句中指定的非聚合列。例如,此查询在标准SQL中是非法的,因为选择列表中的名称列不会出现在GROUP BY中:
SELECT o.custid, c.name, MAX(o.payment)
FROM orders AS o, customers AS c
WHERE o.custid = c.custid
GROUP BY o.custid;
要使查询合法,必须从选择列表中省略name列,或在GROUP BY子句中命名。
MySQL扩展了GROUP BY的使用,因此选择列表可以引用GROUP BY子句中未命名的非聚合列。这意味着前面的查询在MySQL中是合法的。您可以通过避免不必要的列排序和分组来使用此功能来获得更好的性能。但是,当GROUP BY中未命名的每个非聚合列中的所有值对于每个组都相同时,这非常有用。
所以回答你的问题,为什么MySQL这样做最相关的摘录是:
您可以通过避免不必要的列排序和分组来使用此功能来获得更好的性能。但是,当GROUP BY中未命名的每个非聚合列中的所有值对于每个组都相同时,这非常有用。
我总是主张避开这个特定的MySQL扩展,除非你完全理解它。
想象一下下面的简单表格(T):
ID | Column1 | Column2 |
----|---------+----------|
1 | A | X |
2 | A | Y |
在MySQL中你可以写
SELECT ID, Column1, Column2
FROM T
GROUP BY Column1;
这实际上打破了SQL标准,但它适用于MySQL,但问题是它是非确定性的,结果是:
ID | Column1 | Column2 |
----|---------+----------|
1 | A | X |
不比
更正确或更不正确ID | Column1 | Column2 |
----|---------+----------|
2 | A | Y |
所以你要说的是Column1
的每个不同值给我一行,两个结果集都满足,所以你怎么知道你会得到哪一个?好吧你没有,似乎是一个相当流行的误解,你可以添加和ORDER BY
子句来影响结果,所以例如以下查询:
SELECT ID, Column1, Column2
FROM T
GROUP BY Column1
ORDER BY ID DESC;
确保您获得以下结果:
ID | Column1 | Column2 |
----|---------+----------|
2 | A | Y |
因为ORDER BY ID DESC
,但事实并非如此(as demonstrated here)。
服务器可以自由选择每个组中的任何值,因此除非它们相同,否则所选的值是不确定的。此外,添加ORDER BY子句不会影响每个组中值的选择。
因此,即使您有一个订单,但在每个组选择了一行之后才会适用,而且这一行是不确定的。
SQL-Standard允许选择列表中的列不包含在GROUP BY中或聚合函数中,但是这些列必须在功能上依赖于GROUP BY中的列。从SQL-2003-Standard:
15)如果T是分组表,那么让G成为T的分组列的集合 在,引用T列的每个列引用应引用某些C列 在功能上依赖于G或应包含在a的聚合参数中 其聚合查询为QS。
例如,示例表中的ID是PRIMARY KEY,因此我们知道它在表中是唯一的,因此以下查询符合SQL标准并且将在MySQL中运行并且在当前的许多DBMS中失败(当时写作Postgresql是我所知道的最接近正确实施标准的DBMS - Example here):
SELECT ID, Column1, Column2
FROM T
GROUP BY ID;
由于ID对于每一行都是唯一的,因此每个ID只能有一个值Column1
,一个Column2
值,对于每行返回的内容没有歧义。
答案 2 :(得分:3)
group by
是一个非常明确的SQL构造。据我所知,几乎所有数据库都会对等效的SQL查询进行相同的处理。
我可以想到在比较Oracle和MySQL的结果时可能会出现的两个差异。
首先,Oracle将空字符串和NULL
值视为相同。所以Oracle中的以下查询:
select c, count(*)
from (select '' as c from dual union all
select NULL from dual
) t
group by c;
将返回一行,计数为“2”。每个其他数据库(几乎所有其他数据库?)都遵循ANSI标准,并返回两行,计数为1.
第二个区别是MySQL扩展了标准以允许选择中的非聚合列。所以,MySQL将允许这样:
select a, b
from t
group by a;
这会在几乎所有其他数据库中生成语法错误。并且,如果a
不是t
中的唯一列,则此行为违反ANSI标准。如果您遇到语法错误,这可能是您遇到的绊脚石。在这种情况下,Oracle可能正在做正确的事情,你应该学会编写更好的聚合查询。
另一个区别是group by
中结果的排序。 MySQL已弃用此功能,因此任何代码都不应该实际依赖它。但是,除非存在特定的order by
子句,否则结果集本质上是无序的,因此不同顺序的两个结果集将是等效的。