我简化了一个多对多关系的案例 使用这些模型表。
Posts:
------------------------------
| id | title | body |
------------------------------
| 1 | One | text1 |
| 2 | Two | text2 |
| 3 | Three | text3 |
------------------------------
Tags:
-------------------
| id | name |
-------------------
| 1 | SQL |
| 2 | GLSL |
| 3 | PHP |
-------------------
Post_tags:
------------------------------
| id | p_id | t_id |
------------------------------
| 1 | 1 | 1 |
| 2 | 1 | 3 |
| 3 | 2 | 1 |
| 3 | 3 | 2 |
------------------------------
我的目标是查询带有特定标签的帖子,我对此没有问题,但我也想显示帖子的所有相关标签,而不仅仅是我查询的标签。 我的查询如下所示:
SELECT p.Title, p.Body, t.name
FROM Posts p
LEFT JOIN Post_tags pt ON p.id = pt.p_id
LEFT JOIN Tags t ON t.id = pt.t_id
WHERE t.name LIKE '%SQL%'
这会获取带有“SQL”标签的帖子,但它只会在找到“SQL”字符串的位置加入带有标签的帖子表,因此不会加入与帖子关联的其他标签“PHP”。
显然问题是我在 WHERE 子句上连接表,但是如何在一个查询或(最好使用子查询)中解决这个问题?
目前,我在我的应用程序中的两个独立查询中执行此操作,一个用于选择匹配的帖子,另一个用于检索完整的帖子数据。这不是那么有效,而且似乎是一个蹩脚的解决方案,而且我还没有找到更好的解决方案,所以我决定询问 StackOverflow 社区。p>
最佳答案
我的 old answer不是最短的,这是最短的:
select p.*, '' as x, t.name, t.name like '%SQL%'
from Posts p
join Posts_tags pt on pt.p_id = p.id
join Tags t on t.id = pt.t_id;
输出:
ID TITLE BODY X NAME T.NAME LIKE '%SQL%'
1 One text1 SQL 1
1 One text1 PHP 0
2 Two text2 SQL 1
3 Three text3 GLSL 0
因此,如果我们按 ID 分组,并检查组中的元素是否至少有一个(借助于 bit_or;Postgresql 也有这个,恰本地命名为 bool_or)满足 '%SQL%' 条件,它的位为 ON (又名 bool 值 = 真)。我们可以选择该组并保留该组下的所有标签,例如,标签 id 1 出现在帖子 1 上,而帖子 1 有其他标签,即 #3 或 PHP。属于同一帖子 ID 的所有标签都不会被丢弃,因为我们不会使用 WHERE
过滤器,我们将使用 HAVING
过滤器:
select p.*, group_concat(t.name) as tags
from Posts p
join Posts_tags pt on pt.p_id = p.id
join Tags t on t.id = pt.t_id
group by p.id
having bit_or(t.name like '%SQL%');
我们也可以将其重写为:
select p.*, group_concat(t.name) as tags
from Posts p
join Posts_tags pt on pt.p_id = p.id
join Tags t on t.id = pt.t_id
group by p.id
having sum(t.name like '%SQL%') >= 1;
BIT_OR
类似于 IN
或 ANY
,因此它比通过 SUM
求值更具语义。 p>
输出:
D TITLE BODY TAGS
1 One text1 PHP,SQL
2 Two text2 SQL
现场测试:http://www.sqlfiddle.com/#!2/52b3b/26
我在 stackoverflow 上学到了很多东西。在我的旧答案之后,我正在考虑如何通过 SUM OVER partition
使用窗口函数(MySQL 没有)在 Postgresql 中制作一个等效的较短代码。然后想到了Postgresql的bool_or
,bool_and
和every
函数。然后我记得 MySQL 有 bit_or
:-)
最后一个使用 SUM
的解决方案只是事后的想法,当我想到 bit_or
只是 至少一个为真 的语义时,那么显然您也可以使用 HAVING SUM(condition) >= 1
。现在它适用于所有数据库:-)
我最终没有通过窗口函数解决它,上面的解决方案现在适用于所有数据库:-)
关于mysql - 如何查询多对多连接并过滤相同的关系?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10469884/