2

私は大学でデータベースについて学んでおり、大学のコースの最低平均試験成績を見つけるという課題があります。私は2つの解決策を作成しましたが、ここの専門家が私を助けてくれることを願っています:

最善/最も効果的なソリューションは何ですか?

解決策 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 を使用するのが正しいかどうかを考えていましたか?

4

1 に答える 1

5

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

  1. T がグループ化されたテーブルである場合、G を T のグループ化列のセットとする。集計クエリが QS である の集計された引数。
于 2013-09-09T17:02:36.930 に答える