加入Left或WHERE解决方案 - 效率最高?

Nic*_*ick 2 mysql sql database

我正在学习大学数据库,并负责找到大学课程的最低平均考试成绩.我已经提出了两个解决方案,但我希望你们这里的专家可以帮助我:

什么是最好/最有效的解决方案?

解决方案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
Run Code Online (Sandbox Code Playgroud)

解决方案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
Run Code Online (Sandbox Code Playgroud)

而且我一直在考虑JOIN或LEFT JOIN在这里使用是否正确?

Gar*_*thD 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
Run Code Online (Sandbox Code Playgroud)

解决方案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
Run Code Online (Sandbox Code Playgroud)

然而,我会根据你自己的执行计划来检查这一点,因为我只是一个关于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
Run Code Online (Sandbox Code Playgroud)

这样返回一个表:

Name    |   avg_grade
--------+--------------
   A    |       10
   B    |       5
   C    |       6
   D    |       7
   E    |       2
Run Code Online (Sandbox Code Playgroud)

您可能希望整个查询返回:

Name    |   avg_grade
--------+--------------
   E    |       2
Run Code Online (Sandbox Code Playgroud)

由于2是最低平均等级,E是与之对应的名称.你会错的,正如这里所示,你可以看到这实际上会返回:

Name    |   avg_grade
--------+--------------
   A    |       2
Run Code Online (Sandbox Code Playgroud)

实际上正在发生的是MySQL正在正确计算最小avg_grade,但是由于你没有向该组添加任何列,因此你已经给了Name它选择任何值的MySQL Carte blanche .

为了得到你想要的输出,我认为你需要:

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;
Run Code Online (Sandbox Code Playgroud)

或者,如果您只想要平均成绩最低的课程,请使用:

SELECT  courseCode, AVG(grade) as avg_grade
FROM    exams
GROUP BY courseCode
ORDER BY avg_grade
LIMIT 1;
Run Code Online (Sandbox Code Playgroud)

关于SQL小提琴的例子

请原谅我将要做的事情的懒惰,但我之前已经解释了很多这个问题,现在有一个标准的回复我发布以解释MySQL分组的问题.它比上面更详细,希望进一步解释.


MySQL隐式分组

我建议尽可能避免MySQL提供的隐式分组,这意味着包括选择列表中的列,即使它们不包含在聚合函数或group by子句中.

想象一下下面的简单表(T):

ID  | Column1 | Column2  |
----|---------+----------|
1   |    A    |    X     |
2   |    A    |    Y     |
Run Code Online (Sandbox Code Playgroud)

在MySQL中你可以写

SELECT  ID, Column1, Column2
FROM    T
GROUP BY Column1;
Run Code Online (Sandbox Code Playgroud)

这实际上打破了SQL标准,但它适用于MySQL,但问题是它是非确定性的,结果是:

ID  | Column1 | Column2  |
----|---------+----------|
1   |    A    |    X     |
Run Code Online (Sandbox Code Playgroud)

没有或多或少不正确

ID  | Column1 | Column2  |  
----|---------+----------|
2   |    A    |    Y     |
Run Code Online (Sandbox Code Playgroud)

所以你所说的是给我一行,每个不同的值Column1,两个结果集都满足,所以你怎么知道你会得到哪一个?嗯,你没有,似乎是一个相当流行的误解,你可以添加和ORDER BY子句来影响结果,所以例如以下查询:

SELECT  ID, Column1, Column2
FROM    T
GROUP BY Column1
ORDER BY ID DESC;
Run Code Online (Sandbox Code Playgroud)

确保您获得以下结果:

ID  | Column1 | Column2  |  
----|---------+----------|
2   |    A    |    Y     |
Run Code Online (Sandbox Code Playgroud)

因为ORDER BY ID DESC,但事实并非如此(如此处所示).

MySQL的文件状态:

服务器可以自由选择每个组中的任何值,因此除非它们相同,否则所选的值是不确定的.此外,添加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;
Run Code Online (Sandbox Code Playgroud)

由于ID对于每一行都是唯一的,因此Column1每个ID 只能有一个值,一个值Column2对于每行返回的内容没有歧义.

编辑

从SQL-2003-Standard(5WD-02-Foundation-2003-09 - 第346页) - http://www.wiscorp.com/sql_2003_standard.zip

15)如果T是分组表,那么令G为T的分组列的集合.在每个包含的列中,引用T列的每个列引用应引用某些在功能上依赖于G或应包含的列C在聚合查询为QS的聚合参数中.