2013-04-14 286 views
7

最近,我处理了从MySQL数据库中检索大量由数千条记录组成的数据。由于这是我第一次处理这样的大数据集,我没有想到SQL语句的效率。问题就来了。NATURAL JOIN与WHERE IN子句

这里是数据库 的表(这仅仅是一个课程体系的简单数据库模型):

课程:

+-----------+---------------------+------+-----+---------+----------------+ 
| 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 JOINWHERE IN
+2

'select'是一个表的名称。 –

回答

4

理论上这两个查询是等价的。我认为这只是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 |    | 
+----+-------------+-------------------+--------+---------------------+-----------+---------+---------------------------------------+------+-------------+ 

现在它使用索引。

+1

这两个查询不等价。 JOIN将产生与子选择不同的结果。 –

+0

@a_horse_with_no_name如果子查询只对每个'course_id'返回一行,则两者是等价的。如果它可以返回多行,那么连接将生成一个交叉产品,而该入门将仅为每个课程生成一行。 – Barmar

+1

准确。因此,这两个陈述是不一样的(他们显然只是在这种情况下返回相同的结果,这与“等同”不同)。他们的意思是根本不同,@rAy应该意识到这一点。 –

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 
); 

通知的增加和子句中的子查询。这被称为共同相关的子查询,因为它与两个course_id相关,就像NATURAL JOIN一样。

我认为巴马尔的指数解释是在标记上。