我想这一定是一个相当普遍的问题,但我很难找到答案。
如果我有三张桌子:
__POST__
Post_ID Other_stuff
1
2
3
4
...
__TAG__
Tag_ID Tag_name
1 MySQL
2 TSQL
3 PGSQL
4 PHP
5 Java
6 VB.NET
__CATEGORY__
Cat_Id Cat_Description
1 IIS7
2 Apache
3 Oracle
4 NodeJS
__POST_TAG__
Post_ID Tag_Id
1 2
1 6
2 1
2 4
3 4
3 5
3 1
4 1
__POST_CATEGORY__
Post_ID Cat_Id
1 1
2 1
2 2
3 1
3 2
是否可以生成MySQL查询以仅返回使用指定的多个标签和类别标记的帖子?
e.g。在我的前端界面中,用户会选择" MySQL"和" PHP"标签和" IIS7"和#34; Apache"类别和我的查询将返回帖子2和3,但不会返回其他人。用户可能无法选择每个选项中的任何一个或多个。
我能得到的最接近的是
SELECT
distinct p.*
FROM posts p
INNER JOIN post_tag pt on p.Post_Id = pt.Post_Id
INNER JOIN post_category pc on p.Post_Id = pc.Post_Id
WHERE pt.tag_id IN(1,4)
AND ct.cat_id IN(2,3);
但这会导致OR查询,其中 MySQL 或 PHP标记以及 Apache 或的帖子返回IIS7,而我需要匹配所有输入的值。
目前我试图阻止将标签和类别存储为逗号分隔的字符串,因为从标准化的角度来看这似乎是非常糟糕的做法,但至少我可以使用
AND tag like '%MySQL%' AND tag like '%PHP%' AND cat LIKE '%APACHE%'...etc
但这似乎是一个非常不令人满意的解决方案。任何人都可以用更好的方法来帮助我吗?
答案 0 :(得分:1)
我有一段时间没有使用MySQL,但这是一个普遍的问题。
IN (...)
子句是基于OR
的系统。你已经发现了。
您可以使用EXISTS
子查询,这些子查询比IN ()
子查询(假设接近加入速度)快得多,如下所示:
SELECT p.*
FROM posts p
WHERE EXISTS (SELECT 1 FROM post_tag pt WHERE p.Post_Id = pt.Post_Id AND pt.tag_id = 1)
AND EXISTS (SELECT 1 FROM post_tag pt WHERE p.Post_Id = pt.Post_Id AND pt.tag_id = 4)
AND EXISTS (SELECT 1 FROM post_category pc WHERE p.Post_Id = pc.Post_Id AND pc.tag_id = 2)
AND EXISTS (SELECT 1 FROM post_category pc WHERE p.Post_Id = pc.Post_Id AND pc.tag_id = 3);
请注意,如果您不需要返回类别或标记信息,则无需加入。
另一种选择是使用INTERSECT
,但这对于查询引擎来说通常更复杂,并且最终可能与具有子查询的IN ()
子句一样高效。
答案 1 :(得分:0)
将过滤器放在JOIN
子句中可以解决问题:
SELECT p.*
FROM posts p
INNER JOIN post_tag pt1 on p.Post_Id = pt1.Post_Id AND pt1.tag_id = 1
INNER JOIN post_tag pt2 on p.Post_Id = pt2.Post_Id AND pt2.tag_id = 4
INNER JOIN post_category pc1 on p.Post_Id = pc1.Post_Id AND pc1.cat_id = 2
INNER JOIN post_category pc2 on p.Post_Id = pc2.Post_Id AND pc2.cat_id = 3
也可以这样写:
SELECT p.*
FROM posts p
INNER JOIN post_tag pt1 on p.Post_Id = pt1.Post_Id
INNER JOIN post_tag pt2 on p.Post_Id = pt2.Post_Id
INNER JOIN post_category pc1 on p.Post_Id = pc1.Post_Id
INNER JOIN post_category pc2 on p.Post_Id = pc2.Post_Id
WHERE pt1.tag_id = 1
AND pt2.tag_id = 4
AND pc1.cat_id = 2
AND pc2.cat_id = 3