11

最近、MySQL データベースから数千のレコードで構成される大量のデータを取得する作業を行いました。こんなに大きなデータセットを扱うのは初めてだったので、SQL ステートメントの効率については考えていませんでした。そして問題が発生します。

データベースのテーブルは次のとおりです (カリキュラム システムの単純なデータベース モデルです)。

コース:

+-----------+---------------------+------+-----+---------+----------------+
| Field     | Type                | Null | Key | Default | Extra          |
+-----------+---------------------+------+-----+---------+----------------+
| course_id | int(10) unsigned    | NO   | PRI | NULL    | auto_increment |
| name      | varchar(20)         | NO   |     | NULL    |                |
| lecturer  | varchar(20)         | NO   |     | NULL    |                |
| credit    | float               | NO   |     | NULL    |                |
| week_from | tinyint(3) unsigned | NO   |     | NULL    |                |
| week_to   | tinyint(3) unsigned | NO   |     | NULL    |                |
+-----------+---------------------+------+-----+---------+----------------+

選択する:

+-----------+------------------+------+-----+---------+----------------+
| Field     | Type             | Null | Key | Default | Extra          |
+-----------+------------------+------+-----+---------+----------------+
| select_id | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| card_no   | int(10) unsigned | NO   |     | NULL    |                |
| course_id | int(10) unsigned | NO   |     | NULL    |                |
| term      | varchar(7)       | NO   |     | NULL    |                |
+-----------+------------------+------+-----+---------+----------------+

学生が選択したすべてのコースを(カード番号で)取得したい場合、SQLステートメントは次のとおりです。

SELECT course_id, name, lecturer, credit, week_from, week_to
FROM `course` WHERE course_id IN (
    SELECT course_id FROM `select` WHERE card_no=<student's card number>
);

しかし、それは非常に遅く、長い​​間何も返されませんでした。そこで、WHERE IN節をに変更しましNATURAL JOINた。これがSQLです。

SELECT course_id, name, lecturer, credit, week_from, week_to
FROM `select` NATURAL JOIN `course`
WHERE card_no=<student's card number>;

すぐに戻り、正常に動作します!

だから私の質問は:

  • NATURAL JOINWHERE IN節はどう違いますか?
  • それらのパフォーマンスが異なる理由は何ですか? (設定してないからでしょINDEXうか?)
  • orはいつ使用しますNATURAL JOINWHERE IN
4

2 に答える 2

5

理論的には、2 つのクエリは同等です。JOIN が WHERE IN よりも効率的である原因は、MySQL クエリ オプティマイザーの実装が不十分なためだと思います。だから私はいつもJOINを使っています。

2 つのクエリの EXPLAIN の出力を見ましたか? これが私が得たものですWHERE IN

+----+--------------------+-------------------+----------------+-------------------+---------+---------+------------+---------+--------------------------+
|  1 | PRIMARY            | t_users           | ALL            | NULL              | NULL    | NULL    | NULL       | 2458304 | Using where              |
|  2 | DEPENDENT SUBQUERY | t_user_attributes | index_subquery | PRIMARY,attribute | PRIMARY | 13      | func,const |       7 | Using index; Using where |
+----+--------------------+-------------------+----------------+-------------------+---------+---------+------------+---------+--------------------------+

どうやらサブクエリを実行してから、メインテーブルのすべての行を調べて、それが含まれているかどうかをテストしています-インデックスは使用していません。JOIN の場合、次のようになります。

+----+-------------+-------------------+--------+---------------------+-----------+---------+---------------------------------------+------+-------------+
| id | select_type | table             | type   | possible_keys       | key       | key_len | ref                                   | rows | Extra       |
+----+-------------+-------------------+--------+---------------------+-----------+---------+---------------------------------------+------+-------------+
|  1 | SIMPLE      | t_user_attributes | ref    | PRIMARY,attribute   | attribute | 1       | const                                 |   15 | Using where |
|  1 | SIMPLE      | t_users           | eq_ref | username,username_2 | username  | 12      | bbodb_test.t_user_attributes.username |    1 |             |
+----+-------------+-------------------+--------+---------------------+-----------+---------+---------------------------------------+------+-------------+

現在はインデックスを使用しています。

于 2013-04-14T06:04:50.820 に答える
3

これを試して:

SELECT course_id, name, lecturer, credit, week_from, week_to
FROM `course` c
WHERE c.course_id IN (
    SELECT s.course_id 
    FROM `select` s
    WHERE card_no=<student's card number>
    AND   c.course_id = s.course_id
);

サブクエリに AND 句が追加されていることに注意してください。これは、NATURAL JOIN と同様に、2 つの course_id を関連付けるため、相互関連サブクエリと呼ばれます。

Barmar のインデックスの説明は的を射ていると思います。

于 2013-04-14T06:24:03.447 に答える