SQL 如何查询多对多关系

Jie*_*eng 10 doctrine dql

如果帖子和标签之间存在多对多关系,我该如何选择包含特定标签的帖子?

更新:

我遇到的问题是,由于 where tag.name = 'xxx',只选择了那个标签。我想要的是选择所有指定了标签的帖子,tgt 及其所有标签,例如。

Post 1 -> tag1, tag2
Post 2 -> tag1, tag3
Post 3 -> tag2, tag3
Run Code Online (Sandbox Code Playgroud)

目前我得到的是

Post 1 -> tag2 // missing tag1
Post 3 -> tag2 // missing tag3 
Run Code Online (Sandbox Code Playgroud)

cle*_*tus 17

假设这些表:

  • 帖子:ID、作者、日期、内容
  • 标签: id, 名称
  • PostTags: post_id, tag_id

最后一个表通常称为连接表,促进了帖子和标签之间的多对多关系。

SELECT p.*
FROM posts p
JOIN posttags pt ON p.id = pt.post_id
JOIN tags t ON pt.tag_id = t.id
WHERE t.name = 'sql'
Run Code Online (Sandbox Code Playgroud)

基本上,将多对多关系视为两个一对多关系,因为这就是它们在普通 RDBMS 中的实现方式。所以上面的查询有一个从 Posts 到 PostTags 和另一个从 Tags 到 PostTags 的一对多连接。

我创建的 PostTags 表有一个复合主键,即(post_id, tag_id). 这种组合将是独一无二的。许多人不喜欢复合键,因此您经常会看到人们创建主键列:

  • PostTags: id, post_id, tag_id

两种方法都可以。这主要是哲学上的差异。

更新:如果您想选择所有具有特定标签的帖子以及这些帖子的所有标签,则:

SELECT p.*
FROM posts p
JOIN posttags pt ON p.id = pt.post_id
JOIN tags t ON pt.tag_id = t.id
WHERE p.id IN
  (SELECT post_id
  FROM PostTags pt
  JOIN tags t ON pt.tag_id = t.id
  WHERE t.name = 'xyz')
Run Code Online (Sandbox Code Playgroud)

另一种方法是:

SELECT p.*
FROM posts p
JOIN posttags pt ON p.id = pt.post_id
JOIN tags t ON pt.tag_id = t.id
WHERE EXISTS
  (SELECT post_id
  FROM PostTags pt
  JOIN tags t ON pt.tag_id = t.id
  WHERE t.name = 'xyz'
  AND pt.post_id = p.id)
Run Code Online (Sandbox Code Playgroud)

哪个性能更好需要进行测试,并且可能因数据库供应商和版本而异。一个好的优化器(即 Oracle)可能会优化它们以执行相同的操作。其他人可能不会。

现在这会让你像这样返回行:

Post 1, tag 1
Post 1, tag 2
Post 3, tag 2
Post 3, tag 3
Run Code Online (Sandbox Code Playgroud)

所以你需要将它们组合起来,最好是在应用程序逻辑而不是 SQL 中。某些 RDBMS 具有针对此类事物的特定于供应商的扩展,例如 MySQL 的GROUP_CONCAT()功能。