2009-10-09 54 views
2

如果我運行以下查詢,每個人都會快速返回(0.01秒),併爲我提供所需的結果。使用IN謂詞的MySQL性能

SELECT tagId FROM tag WHERE name='programming'

SELECT COUNT(DISTINCT workcode) FROM worktag WHERE tagId=123 OR tagId=124

(假設這兩個標籤識別數字是從第一個查詢的結果)

我想,所以我只需要一次運行這些查詢結合:

SELECT COUNT(DISTINCT workcode) FROM worktag WHERE tagId IN (SELECT tagId FROM tag WHERE name='programming')

然而,這個查詢在大約1分鐘和20分鐘內完成秒。我有索引worktag.workcode,worktag.tagId,tag.tagIdtag.name

如果我在查詢上運行DESCRIBE,前兩個使用索引,第二個使用子查詢的索引(在tag表上),但不使用worktag表上的任何索引。

有誰知道這可能是爲什麼?

注意:worktag表中有超過1800萬條記錄。

+0

僅供參考:MySQL中的IN vs範圍:http://explainextended.com/2009/10/07/in-list-vs-range-condition-mysql/ – 2009-10-09 21:03:15

回答

2

爲什麼不使用連接而不是子查詢?

SELECT COUNT(DISTINCT workcode) 
FROM worktag 
LEFT JOIN tag 
    ON worktag.tagId = tag.tagID 
WHERE tag.name = 'programming' 

P.S .:似乎是reported as bug

+0

我的同事剛過來給我完全一樣的解。據他介紹,mysql可以更容易地將JOIN優化爲子查詢。我不太喜歡這種說法(子查詢對我來說很好看),但它效果很好。謝謝。 – chadgh 2009-10-09 20:30:10

+0

子查詢更爲重要:您明確要求先選擇ID,然後根據以前的結果選擇計數。在我看來,這給予優化者更少的自由。 (儘管你的問題似乎仍然是一個錯誤,而不是EBKAC!:) – Zed 2009-10-09 20:35:51

0

你試過:

SELECT COUNT(DISTINCT workcode) FROM worktag WHERE tagId IN (123, 124) 

我不是MySQL專家,但是在我看來,您可能正在查看查詢優化器的重大故障。

另一方面,對MySQL有利,它在第二個語句中優化了OR。我知道將成功優化IN()的數據庫,但不知道相同邏輯請求的OR版本。

1

最近數據庫管理員告訴我,語法WHERE x IN (...)是數據庫的痛處。一個連接是幾乎總是更好:

SELECT COUNT(DISTINCT wt.workcode) 
    FROM worktag wt, tag t 
WHERE wt.tagId = t.tagId 
    AND t.name='programming' 
+0

它不一定是,至少在Oracle和Sql服務器上,這是一個神話。 OP在MySQL中遇到了一些不良行爲。你的觀點對於MySQL來說可能是正確的,沒有什麼內在的IN慢。 – erikkallen 2009-10-09 20:33:56

+0

您需要告訴您的數據庫管理員,該語句高度依賴於產品。大量的數據庫使用IN比使用JOIN或OR更好。我看不出爲什麼IN()會成爲數據庫的痛苦的任何特殊原因,它似乎自然提供了大量的索引訪問機會。 – 2009-10-09 22:15:55

1
SELECT COUNT(DISTINCT workcode) 
FROM worktag 
inner join tag on worktag.tagid = tag.tagid 
WHERE tag.name='programming' 
0

我猜的優化做了一些不好的猜測。用內連接替換查詢可能會有所幫助。

1

對於子查詢,即使是獨立的,MySQL通常也做得不好。討論連接的海報是正確的 - 如果您有選擇,請使用連接。如果你不能輕鬆使用連接(例如,foo.x in(從y = xxx limit 10的bar中選擇y)),最好將限制運行到臨時IN MEMORY表中並使用連接。

如果您使用的MySQL很多,請使用EXPLAIN,您會看到它是如何使用索引等的。