2011-01-10 73 views
2

嚴重編輯!MYSQL連接結果集在IN()where子句中設置了擦除結果嗎?

最初的問題是基於對IN()如何處理來自連接結果集的列的誤解。我認爲IN(some_join.some_column)會將結果列視爲列表並循環遍歷每行。事實證明它只看第一行。

所以,適應的問題:MySQL中是否有任何可以循環訪問WHERE子句中的連接結果列?

下面是我正在使用的超簡化代碼,從複雜的crm搜索功能中剝離下來。左連接和一般想法是來自該查詢的遺留物。所以對於這個查詢,它必須是一個獨家搜索 - 找到所有指定標籤的人,而不是任何人。

首先DB

表1:人

+----+------+ 
| id | name | 
+----+------+ 
| 1 | Bob | 
| 2 | Jill | 
+----+------+ 

表2:標籤

+-----------+--------+ 
| person_id | tag_id | 
+-----------+--------+ 
|   1 |  1 | 
|   1 |  2 | 
|   2 |  2 | 
|   2 |  3 | 
+-----------+--------+ 

尼斯和簡單。所以,自然:

SELECT name, GROUP_CONCAT(tag.tag_id) FROM person LEFT JOIN tag ON person.id = tag.person_id GROUP BY name; 
+------+--------------------------+ 
| name | GROUP_CONCAT(tag.tag_id) | 
+------+--------------------------+ 
| Bob | 1,2      | 
| Jill | 2,3      | 
+------+--------------------------+ 

到目前爲止好。所以我在尋找的是在第一種情況下只能找到Bob,而在第二種情況下只能找到Jill的情況 - 不使用HAVING COUNT(DISTINCT ...),因爲這在更廣泛的查詢中不起作用(有單獨的標籤繼承緩存和大量其他東西)。

這是我的原始示例查詢 - 基於IN()會一次遍歷所有行的錯誤想法。

SELECT DISTINCT name FROM person LEFT JOIN tag ON person.id = tag.person_id 
    WHERE ((1 IN (tag.tag_id)) AND (2 IN (tag.tag_id)));        
Empty set (0.00 sec) 

SELECT DISTINCT name FROM person LEFT JOIN tag ON person.id = tag.person_id 
    WHERE ((2 IN (tag.tag_id)) AND (3 IN (tag.tag_id))); 
Empty set (0.00 sec) 

這是我的新最新的嘗試失敗給什麼我的目標......

SELECT name, GROUP_CONCAT(tag.tag_id) FROM person LEFT JOIN tag ON person.id = tag.person_id 
    GROUP BY person.id HAVING ((1 IN (GROUP_CONCAT(tag.tag_id)))) AND (2 IN (GROUP_CONCAT(tag.tag_id))); 
Empty set (0.00 sec) 

所以看起來它採取GROUP_CONCAT串,任1,2或2個主意,3,並將其視爲單一實體而不是表達式列表。有沒有辦法將分組列轉換爲IN()或= ANY()將視爲列表的表達式列表?

本質上,我試圖使IN()循環迭代地處理類似於數組或動態表達式列表的東西,該列表包含來自聯接的所有數據行。

+0

所以多一點測試,看起來這些結果是相同的,如果我使用=而不是IN。所以,也許MySQL優化器正在將我的'IN'轉換爲'=',看到這給出了「P = X AND P = Y」,並因此返回了幾乎沒有觸及數據庫的空結果集 - 因此超級快速響應時間<0.01秒? – user568458 2011-01-10 16:44:27

+0

超快響應時間可能是由於WHERE條件永遠不會成立,因此不需要讀取任何行。看到喬的回答。 – 2011-01-10 16:50:16

回答

4

想想你的代碼邏輯做:

(1 IN (tag.tag_id)) AND (2 IN (tag.tag_id)) 

相當於

(1 = (tag.tag_id)) AND (2 = (tag.tag_id)) 

有沒有辦法tag.tag_id可以同時滿足這兩個條件,所以,與是不正確的。

它看起來就像你在你的問題中引用的或版本是你真正想要的:

SELECT DISTINCT name FROM person LEFT JOIN tag ON person.id = tag.person_id 
    WHERE ((1 IN (tag.tag_id)) OR (2 IN (tag.tag_id))); 

更恰當地使用IN子句,你可以寫爲:

SELECT DISTINCT name FROM person LEFT JOIN tag ON person.id = tag.person_id 
    WHERE tag.tag_id in (1,2); 

一最後一點,因爲你在WHERE子句(tag.tag_id)中引用了來自LEFT JOINed表的列,所以你真的強迫它像INNER JOIN那樣工作。要真正得到一個LEFT JOIN,你需要移動的標準出了其中,並使其的JOIN條件的一部分,而不是:

SELECT DISTINCT name FROM person LEFT JOIN tag ON person.id = tag.person_id 
    AND tag.tag_id in (1,2); 
+0

是的,聽起來像我誤解了IN()如何工作的結果集 - 我認爲它會將列視爲一個循環的集合,而不是作爲一個單一的行。那麼,是否有任何MySQL函數可以工作,我希望IN如何工作?例如,我可以輸入GROUP_CONCAT的結果嗎? – user568458 2011-01-10 16:57:30

0
WHERE ((1 IN (tag.tag_id)) AND (2 IN (tag.tag_id)));  

這不會返回任何結果,因爲tag.tag_id不能同時爲1和2。

此外,還有一個原因,你使用1 IN (blah)而不是blah = 1