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

时间:2013-09-09 16:12:59

标签: 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

解决方案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在这里使用是否正确?

1 个答案:

答案 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)。

MySQL documents州:

  

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