2013-04-14 170 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一樣。

我認爲巴馬爾的指數解釋是在標記上。