2009-09-16 82 views
4

我有一個表files與文件和表reades讀取這些文件的訪問。在表reades中有一列file_id,其中涉及files中的相應列。現在應該是獨立的子查詢不是。爲什麼?

我想列出還沒有被存取,並嘗試這樣所有文件:

SELECT * FROM files WHERE file_id NOT IN (SELECT file_id FROM reades) 

這是非常緩慢的。原因是mySQL認爲子查詢依賴於查詢:

+----+--------------------+--------+------+---------------+------+---------+------+------+----------+-------------+ 
| id | select_type  | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra  | 
+----+--------------------+--------+------+---------------+------+---------+------+------+----------+-------------+ 
| 1 | PRIMARY   | files | ALL | NULL   | NULL | NULL | NULL | 1053 | 100.00 | Using where | 
| 2 | DEPENDENT SUBQUERY | reades | ALL | NULL   | NULL | NULL | NULL | 3242 | 100.00 | Using where | 
+----+--------------------+--------+------+---------------+------+---------+------+------+----------+-------------+ 

但是爲什麼?子查詢是完全獨立的,或多或少只是爲了返回一個ID列表。

(準確地說:每個file_id可以reades出現多次,當然,因爲可以有用於每個文件任意多個讀取操作。)

+0

這是MySQL中的一個[bug](http://bugs.mysql.com/bug.php?id=63701)。它是從MySQL 5.6.3開始修復的。 – rustyx 2015-01-23 10:48:05

回答

4

嘗試用加入替換子查詢:

SELECT * 
FROM files f 
LEFT OUTER JOIN reades r on r.file_id = f.file_id 
WHERE r.file_id IS NULL 

下面是一個article about this problem的鏈接。那篇文章的作者寫了一個存儲過程來強制MySQL將子查詢評估爲獨立的。儘管如此,我懷疑這是必要的。

+0

嗯。 EXPLAIN標識沒有相關的查詢,但它仍然非常慢。 – fuenfundachtzig 2009-09-16 18:27:42

+0

這應該在任何體面的機器上都很快。考慮在reades.file_id上​​添加一個索引,或將其作爲外鍵。 – Andomar 2009-09-16 18:29:16

+0

我添加了一個外鍵(ALTER TABLE reades ADD FOREIGN KEY(file_id)REFERENCES files(file_id);),現在速度更快。 (儘管我認爲mySQL不支持外鍵?!) – fuenfundachtzig 2009-09-16 18:38:03

2

嘗試:

SELECT * FROM files WHERE file_id NOT IN (SELECT reades.file_id FROM reades) 

即:如果它是作爲依賴而出現的,也許這是因爲file_id所指的含糊不清,所以讓我們嘗試對其進行完全限定。

如果不工作,只是做:

SELECT files.* 
FROM files 
LEFT JOIN reades 
USING (file_id) 
WHERE reades.file_id IS NULL 
+0

好的。是的,我不知道爲什麼會這樣。 – chaos 2009-09-16 18:26:24

+0

第一個建議也被認爲是一個從屬子查詢。第二個作品,但速度很慢。 – fuenfundachtzig 2009-09-16 18:28:36

+0

好吧,在我看來,MySQL解析器故意誤解了查詢 - 或者註釋'DEPENDENT SUBQUERY'意思是與'相關子查詢'不同,它是通常用於依賴於主查詢中的'當前行值'。 – 2009-09-16 18:28:53

4

我以前見過這個。這是一個在MySQL中的錯誤。試試這個:

SELECT * FROM files WHERE file_id NOT IN (SELECT * FROM (SELECT file_id FROM reades)) 

有錯誤報告是在這裏:http://bugs.mysql.com/bug.php?id=25926

+1

找到它:http://bugs.mysql.com/bug.php?id=25926 – longneck 2009-09-16 18:36:04

+0

+1有趣的鏈接;你可以編輯你的問題,而不是添加鏈接作爲評論。 – Andomar 2009-09-16 18:39:18

0

是否支持MySQL以同樣的方式存在MSSQL會嗎? 如果是這樣,你可以重寫查詢作爲

SELECT * FROM文件作爲F其中FILE_ID NOT EXISTS(SELECT 1 FROM reades R其中r.file_id = f.file_id)使用

是因爲它效率極其低下爲父查詢中的每一行運行該子查詢。

0

看着this page我發現了兩種可行的解決方案,它們都可以工作。只是爲了完整性我添加其中的一個,類似於上述連接顯示的答案,但它的速度很快,即使沒有使用外鍵:

SELECT * FROM files AS f 
    INNER JOIN (SELECT DISTINCT file_id FROM reades) AS r 
    ON f.file_id = r.file_id 

這解決了問題,但卻仍不能回答我的問題:)

編輯:如果我解釋正確解釋輸出,這是快速的,因爲解釋器生成臨時索引:

+----+-------------+------------+--------+---------------+---------+---------+-----------+------+--------------------------+ 
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows | Extra     | 
+----+-------------+------------+--------+---------------+---------+---------+-----------+------+--------------------------+ 
| 1 | PRIMARY  | <derived2> | ALL | NULL   | NULL | NULL | NULL  | 843 |       | 
| 1 | PRIMARY  | f   | eq_ref | PRIMARY  | PRIMARY | 4  | r.file_id | 1 |       | 
| 2 | DERIVED  | reades  | range | NULL   | file_id | 5  | NULL  | 811 | Using index for group-by | 
+----+-------------+------------+--------+---------------+---------+---------+-----------+------+--------------------------+ 
+0

如果您必須爲您的'READES.file_id'運行DISTINCT,那是我們不知道的信息。 – 2009-09-16 18:42:51

+0

「MySQL有一個馬虎的查詢優化器」是否回答你的問題? :P – Andomar 2009-09-16 18:49:49

+0

我不會想象索引列會在這種查詢中產生這樣的影響......我認爲mySQL會將子查詢識別爲常量,但顯然必須注意這些假設。 – fuenfundachtzig 2009-09-16 19:09:56

0

IN-子查詢是在MySQL 5.5和更早轉換爲EXIST子查詢。在給定的查詢將被轉換爲以下查詢:

SELECT * FROM文件 WHERE NOT EXISTS(SELECT 1 FROM reades WHERE reades.filed_id = files.file_id)

正如你看到的,子查詢實際上是依賴。

MySQL 5.6可能會選擇實現子查詢。也就是說,首先運行內部查詢並將結果存儲在臨時表中(刪除重複項)。然後,它可以在外部表(即文件)和臨時表之間使用類似連接的操作來查找不匹配的行。如果reades.file_id沒有編入索引,這種執行查詢的方式可能會更理想。

但是,如果對reades.file_id進行索引,傳統的IN-EXISTS執行策略實際上非常高效。在這種情況下,我不希望將查詢轉換爲連接的性能得到顯着提升,正如其他答案中所建議的那樣。 MySQL 5.6優化器在實現和IN-EXISTS執行之間進行基於成本的選擇。

相關問題