您的两个查询不同,因此您无法真正比较效率,您的第二个查询将返回没有考试结果的课程记录。假设您将 LEFT JOIN 切换为 INNER 以使查询具有可比性,那么我希望第一个查询效率更高一些,因为它只有一个派生表,而第二个有两个:
解决方案1:
ID SELECT_TYPE TABLE TYPE POSSIBLE_KEYS KEY KEY_LEN REF ROWS FILTERED EXTRA
1 PRIMARY ALL 5 100
1 PRIMARY courses ALL 5 100 Using where; Using join buffer
2 DERIVED exams ALL 5 100 Using temporary; Using filesort
解决方案2:
ID SELECT_TYPE TABLE TYPE POSSIBLE_KEYS KEY KEY_LEN REF ROWS FILTERED EXTRA
1 PRIMARY ALL 5 100
2 DERIVED courses ALL 5 100 Using temporary; Using filesort
2 DERIVED exams ALL 5 100 Using where; Using join buffer
但是,我会根据您自己的执行计划检查这一点,因为我的只是SQL Fiddle上的一个简单示例。
我想借此机会建议不要使用 ANSI-89 隐式连接语法,它在 20 多年前被 ANSI-92 标准中的显式连接语法所取代。Aaron Bertrand 写了一篇关于为什么要切换的好文章,我不会在这里重复。
另一个更重要的一点是您的查询不是确定性的,也就是说,即使数据没有潜在的变化,您也可以运行两次相同的查询并获得 2 个不同的结果。
以您的第二个查询为例(尽管您会注意到 SQL-Fiddle 上的两个查询都是错误的),您有一个MyTable
像这样的子查询:
SELECT courses.name, AVG(grade) as avg_grade
FROM courses
LEFT JOIN exams on exams.courseCode = courses.code
GROUP BY courseCode
这返回了一个像这样的表:
Name | avg_grade
--------+--------------
A | 10
B | 5
C | 6
D | 7
E | 2
您可能希望整个查询返回:
Name | avg_grade
--------+--------------
E | 2
因为 2 是最低平均成绩,而 E 是与之对应的名称。但是,您会错的,如此处所示,您可以看到它实际上返回了:
Name | avg_grade
--------+--------------
A | 2
本质上发生的是 MySQL 正在正确计算最小 avg_grade,但是由于您没有向组中添加任何列,因此您已经让 MySQL 全权委托选择Name
它选择的任何值。
要获得您想要的输出,我认为您需要:
SELECT courses.name , MIN(avg_grade)
FROM ( SELECT courseCode, AVG(grade) as avg_grade
FROM exams
GROUP BY courseCode
) avg_grades
INNER JOIN courses
ON courses.code = avg_grades.courseCode
GROUP BY courses.Name;
或者,如果您只想学习平均成绩最低的课程,请使用:
SELECT courseCode, AVG(grade) as avg_grade
FROM exams
GROUP BY courseCode
ORDER BY avg_grade
LIMIT 1;
SQL Fiddle 上的示例
请原谅我即将做的事情的懒惰,但我之前已经解释了很多这个问题,现在我发布了一个标准回复来解释 MySQL 分组的问题。它比上面更详细,希望能进一步解释。
MySQL 隐式分组
我建议尽可能避免 MySQL 提供的隐式分组,我的意思是在选择列表中包含列,即使它们不包含在聚合函数或 group by 子句中。
想象一下下面的简单表(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
, 然而这不是真的(如此处所示)。
MySQL文档状态:
服务器可以从每个组中自由选择任何值,因此除非它们相同,否则选择的值是不确定的。此外,从每个组中选择值不会受到添加 ORDER BY 子句的影响。
因此,即使您有这样的订单,直到每组选择一行之后才适用,并且这一行是不确定的。
SQL 标准确实允许选择列表中的列不包含在 GROUP BY 或聚合函数中,但是这些列必须在功能上依赖于 GROUP BY 中的列。例如,示例表中的 ID 是 PRIMARY KEY,所以我们知道它在表中是唯一的,所以下面的查询符合 SQL 标准,在 MySQL 中运行,目前在许多 DBMS 中会失败(在编写 Postgresql 时)是我所知道的最接近正确实施标准的 DBMS):
SELECT ID, Column1, Column2
FROM T
GROUP BY ID;
由于每一行的 ID 是唯一的,因此每个 ID 只能有一个值Column1
,一个值Column2
对于每一行返回的内容没有歧义。
编辑
来自 SQL-2003-Standard (5WD-02-Foundation-2003-09 - 第 346 页) - http://www.wiscorp.com/sql_2003_standard.zip
- 如果 T 是一个分组表,则令 G 是 T 的分组列的集合。在每个包含在 中,每个引用 T 的列的列引用都应引用在功能上依赖于 G 或应包含在一个列中的某个列 C聚合查询为 QS 的 a 的聚合参数。