我正在学习大学数据库,并负责找到大学课程的最低平均考试成绩。我已经提出了两个解决方案,但我希望你们这里的专家可以帮助我:
什么是最好/最有效的解决方案?
解决方案1:
SELECT courses.name , MIN(avg_grade)
FROM (SELECT courseCode, AVG(grade) as avg_grade
FROM exams
GROUP BY courseCode) avg_grades, courses
WHERE courses.code = avg_grades.courseCode
解决方案2:
SELECT name, min(avg_grade)
FROM (SELECT courses.name, AVG(grade) as avg_grade
FROM courses
LEFT JOIN exams on exams.courseCode = courses.code
GROUP BY courseCode) mytable
我一直在考虑JOIN或LEFT JOIN在这里使用是否正确?
答案 0 :(得分:5)
您的两个查询不同,因此您无法真正比较效率,您的第二个查询将返回没有考试结果的课程记录。 假设您将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写了一篇关于切换原因的great article,我不会在这里复制。
另一个更重要的一点是,您的查询不是确定性的,也就是说您可以运行相同的查询两次并获得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是与之对应的名称。你会错的,as demonstrated here你可以看到它实际上会返回:
Name | avg_grade
--------+--------------
A | 2
本质上正在发生的是MySQL正在正确计算最小avg_grade,但由于您没有向该组添加任何列,因此您已选择MySQL Carte blanche为其选择的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;
<强> Examples on 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
,但事实并非如此(as demonstrated here)。
服务器可以自由选择每个组中的任何值,因此除非它们相同,否则所选的值是不确定的。此外,添加ORDER BY子句不会影响每个组中值的选择。
因此,即使你有一个订单,直到每个组选择了一行之后才会适用,而且这一行是不确定的。
SQL-Standard允许选择列表中的列不包含在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
15)如果T是分组表,那么让G成为T的分组列的集合 在,引用T列的每个列引用应引用某些C列 在功能上依赖于G或应包含在a的聚合参数中 其聚合查询为QS。