2013-04-01 45 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'语句中来解决这个问题)。 –