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吗?)
  • 我们什么时候使用NATURAL JOINor WHERE IN
4

2 回答 2

5

理论上这两个查询是等价的。我认为这只是 MySQL 查询优化器的糟糕实现导致 JOIN 比 WHERE IN 更有效。所以我总是使用JOIN。

您是否查看过 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 子句。这被称为关联子查询,因为它关联了两个 course_id,就像 NATURAL JOIN 一样。

我认为 Barmar 的指数解释是正确的。

于 2013-04-14T06:24:03.447 回答