MySQL - 它可以处理多对多关系中的复杂“AND”子句吗?

时间:2014-04-09 14:04:38

标签: mysql sql many-to-many

我想这一定是一个相当普遍的问题,但我很难找到答案。

如果我有三张桌子:

__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

但这似乎是一个非常不令人满意的解决方案。任何人都可以用更好的方法来帮助我吗?

2 个答案:

答案 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
相关问题