2 つのクエリは異なるため、効率を実際に比較することはできません。2 番目のクエリは、試験結果のないコースのレコードを返します。LEFT JOIN を INNER に切り替えてクエリを比較可能にすると仮定すると、最初のクエリには派生テーブルが 1 つしかなく、2 つ目のクエリには 2 つの派生テーブルがあるため、わずかに効率的であると予想されます。
解決策 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 回実行すると、データに根本的な変更がなくても 2 つの異なる結果が得られる可能性があります。
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 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;
SQL Fiddle の例
私がやろうとしていることの怠惰を許してください. 上記よりも詳細に説明されており、うまくいけばさらに説明されます。
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 |
つまり、あなたが言っているのは、 の個別の値ごとに 1 つの行を与えるということですColumn1
。これは両方の結果セットが満たすものです。結果に影響を与えるために and 句を追加できるというのは、かなり一般的な誤解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 句を追加しても影響を受けません。
したがって、これによる順序があっても、グループごとに 1 つの行が選択されるまで適用されず、この 1 つの行は非決定論的です。
SQL 標準では、GROUP BY または集計関数に含まれていない列を選択リストに含めることができますが、これらの列は GROUP BY の列に機能的に依存している必要があります。たとえば、サンプル テーブルの ID は PRIMARY KEY であるため、テーブル内で一意であることがわかっているため、次のクエリは SQL 標準に準拠しており、MySQL で実行され、現在多くの DBMS で失敗します (Postgresql の執筆時点)。私が知っている中で、標準を正しく実装するのに最も近い DBMS です):
SELECT ID, Column1, Column2
FROM T
GROUP BY ID;
ID は行ごとに一意であるColumn1
ため、ID ごとにの値は 1 つしか存在できません。 の 1 つの値はColumn2
、行ごとに何を返すかについてあいまいさはありません。
編集
SQL-2003-Standard から (5WD-02-Foundation-2003-09 - 346 ページ) - http://www.wiscorp.com/sql_2003_standard.zip
- T がグループ化されたテーブルである場合、G を T のグループ化列のセットとする。集計クエリが QS である の集計された引数。