2012-05-15 71 views
3

我們假設MySQL中的以下表格描述文件夾中包含的文檔。如何將NOT IN子查詢重寫爲連接

mysql> select * from folder; 
+----+----------------+ 
| ID | PATH   | 
+----+----------------+ 
| 1 | matches/1  | 
| 2 | matches/2  | 
| 3 | shared/3  | 
| 4 | no/match/4  | 
| 5 | unreferenced/5 | 
+----+----------------+ 


mysql> select * from DOC; 
+----+------+------------+ 
| ID | F_ID | DATE  | 
+----+------+------------+ 
| 1 | 1 | 2000-01-01 | 
| 2 | 2 | 2000-01-02 | 
| 3 | 2 | 2000-01-03 | 
| 4 | 3 | 2000-01-04 | 
| 5 | 3 | 2000-01-05 | 
| 6 | 3 | 2000-01-06 | 
| 7 | 4 | 2000-01-07 | 
| 8 | 4 | 2000-01-08 | 
| 9 | 4 | 2000-01-09 | 
| 10 | 4 | 2000-01-10 | 
+----+------+------------+ 

列ID是主鍵,表DOC的列F_ID是引用表FOLDER的主鍵的非空外鍵。通過在where子句中使用文檔的'DATE',我想查找哪些文件夾只包含選定的文檔。對於除2000-01-05較早版本的文檔,這可以寫成:

SELECT DISTINCT d1.F_ID 
FROM DOC d1 
WHERE d1.DATE < '2000-01-05' 
AND d1.F_ID NOT IN (
    SELECT d2.F_ID 
    FROM DOC d2 WHERE NOT (d2.DATE < '2000-01-05') 
); 

並正確返回「1」和「2」。通過閱讀 http://dev.mysql.com/doc/refman/5.5/en/rewriting-subqueries.html 如果將子查詢替換爲連接,則可以提高大表的性能。我已經發現了與NOT IN和JOINS相關的問題,但並不完全符合我的要求。那麼,關於如何用連接書寫的想法呢?

回答

2
select distinct d1.F_ID 
from DOC d1 
left outer join (
    select F_ID 
    from DOC 
    where date >= '2000-01-05' 
) d2 on d1.F_ID = d2.F_ID 
where d1.date < '2000-01-05' 
    and d2.F_ID is null 
+0

正確和超快速的響應!雖然我沒有問,但我想我不能避免重複'2000-01-05'。 –

+1

子查詢連接的性能是否比'WHERE'上的子查詢更好? – bfavaretto

4

一般的答案是:

select t.* 
from t left outer join 
    (select distinct id from s) s 
    on t.id = s.id 
where s.id is null 

我想你可以將此您的情況:如

select t.* 
from t 
where t.id not in (select id from s) 

可以重寫。

1

如果我正確理解你的問題,你要查找的F_IDs代表只包含「2000-01-05」收到的文件夾,然後只需

SELECT F_ID 
FROM DOC 
GROUP BY F_ID 
HAVING MAX(DATE) < '2000-01-05' 
+0

是的,這是我所要求的,這確實是一個更簡單和正確的查詢。但是,現實是DOC表格有更多列,where子句將以編程方式生成。在我看來,更容易動態添加'where'子句而不是'having',但是我會用大表檢查性能並嘗試一下。謝謝 ! –

+0

您的歡迎,我會給這個閱讀以及http://dev.mysql.com/doc/refman/5.5/en/execution-plan-information.html。另外,除了你在這裏公開的子集外,我不知道你的模式如何。但我傾向於按照經驗法則「如果我需要子查詢,我應該重新訪問我的數據模型」。換句話說,也許規範化你的模型可能會讓事情更簡潔/更容易實現:) – JustDanyul