2013-04-01 48 views
0

我有以下表格:如何爲子查詢中的每一行添加WHERE子句?

CREATE TABLE `content` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `content` varchar(255) NOT NULL, 
    `tag_a_id` int unsigned DEFAULT NULL, 
    `tag_b_id` int unsigned DEFAULT NULL, 
    `tag_c_id` int unsigned DEFAULT NULL, 
    `tag_d_id` int unsigned DEFAULT NULL, 
    `tag_e_id` int unsigned DEFAULT NULL, 
    PRIMARY KEY (`id`) 
); 
CREATE TABLE `tags` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `tag` varchar(32) NOT NULL UNIQUE, 
    PRIMARY KEY (`id`) 
); 

tags表中有內容表中的一個一對多的關係,使用tag_?_id領域,但每個標籤的ID將只能出現一次,每行。

我想要做一個查詢,其中我選擇content表中與給定標記集(以及所有關聯的標記)關聯的所有行。例如,「讓我把所有具有標籤」News「和」MedicalCare「的內容行關聯起來

這意味着需要在tags表中查找」News「和」MedicalCare「的ID,然後注入的content表的查詢,使用一對這樣的查詢(假設這些標籤具有的ID 4568):

SELECT id FROM tags WHERE tag IN ("News","MedicalCare"); 

...然後...

SELECT t1.id, t1.content, ts_a.tag, ts_b.tag, ts_c.tag, ts_d.tag, ts_e.tag 
FROM (
    SELECT t.id, t.content, t.tag_a_id, t.tag_b_id, t.tag_c_id, t.tag_d_id, t.tag_e_id 
    FROM content t 
    WHERE 45 IN (t.tag_a_id, t.tag_b_id, t.tag_c_id, t.tag_d_id, t.tag_e_id) 
    AND 68 IN (t.tag_a_id, t.tag_b_id, t.tag_c_id, t.tag_d_id, t.tag_e_id) 
    ORDER BY t.id ASC LIMIT 200 
) t1 
LEFT JOIN tags ts_a ON t1.tag_a_id=ts_a.id 
LEFT JOIN tags ts_b ON t1.tag_b_id=ts_b.id 
LEFT JOIN tags ts_c ON t1.tag_c_id=ts_c.id 
LEFT JOIN tags ts_d ON t1.tag_d_id=ts_d.id 
LEFT JOIN tags ts_e ON t1.tag_e_id=ts_e.id; 

我有一種方法嗎?取我在這個查詢中感興趣的標籤ID,並動態生成那些AND x IN(a,b,c)子句?

另一種選擇可能是這樣的:

WHERE EVERY ONE OF (
    SELECT id FROM tags WHERE tag IN ("News","MedicalCare") 
) IN (t.tag_a_id, t.tag_b_id, t.tag_c_id, t.tag_d_id, t.tag_e_id) 

請注意:content表是非常大的,所以它是不可行加入內容表標記表中沒有先過濾掉不需要的行和應用LIMIT

+1

您確定要限制特定內容項最多有5個標籤?如果你想要更多,會發生什麼?如果80%的內容只有1-2個標籤會發生什麼?您可能需要考慮添加一個鏈接到content_id和tag_id的內容標籤表。 –

+0

感謝您的建議 - 這是我在我的數據庫的原始設計中考慮的事情。但是,內容表會進入數百萬行,因此加入到多對多表以便按標記過濾結果非常昂貴。 – Alex

+0

不是。如果它是我的分貝,我會規範化它。 –

回答

1

我想這會做到這一點:

select t1.id, t1.content, ts_a.tag, ts_b.tag, ts_c.tag, ts_d.tag, ts_e.tag 
from content t1 
LEFT JOIN tags ts_a ON t1.tag_a_id=ts_a.id 
LEFT JOIN tags ts_b ON t1.tag_b_id=ts_b.id 
LEFT JOIN tags ts_c ON t1.tag_c_id=ts_c.id 
LEFT JOIN tags ts_d ON t1.tag_d_id=ts_d.id 
LEFT JOIN tags ts_e ON t1.tag_e_id=ts_e.id 
where "News" in (ts_a.tag, ts_b.tag, ts_c.tag, ts_d.tag, ts_e.tag) 
and "MedicalCare" in (ts_a.tag, ts_b.tag, ts_c.tag, ts_d.tag, ts_e.tag) 
+0

謝謝Barmar,我同意,我認爲這會起作用,但恐怕我的問題起初缺乏重要的一部分額外信息,我的歉意 - 內容表非常大,因此在從內容中濾除不需要的行之前執行連接桌子非常昂貴。 – Alex

+0

接受了這個答案,因爲事實證明我誤解了我的原始查詢的昂貴部分。 D'哦。 – Alex

+0

我認爲你的原始查詢也可以做你想做的。用子查詢替換硬編碼的45和68,例如'WHERE(SELECT id FROM contents ...)IN(tag_a_id,tag_b_id,...)' – Barmar

1

如果你知道標籤是唯一的,你可以這樣做:

where ((ts_a.tag in ('News', 'MecialCare')) + 
     (ts_b.tag in ('News', 'MecialCare')) + 
     (ts_c.tag in ('News', 'MecialCare')) + 
     (ts_d.tag in ('News', 'MecialCare')) + 
     (ts_e.tag in ('News', 'MecialCare')) 
    ) = 2 

這將使用MySQL中的比較返回0或1的事實,然後可以加在一起。

順便說一句,這個問題是數據應該正確結構化的一個很好的理由,每個人每個標籤都有一個單獨的行,一個persno_tag表。

+0

我不確定我關注。內容表中的5個標記字段是ID,它們引用標記表中的行。如果我理解正確,我需要去5個相同的子查詢來代替'('News','MedicalCare')'? – Alex

+0

@Alex。 。 。如果修復了代碼。但我也喜歡BarMar的方法。他使用標準的SQL,而我正在使用MySQL特有的功能(儘管我可以通過將所有內容都包裝在'case'語句中來解決這個問題)。 –