SQL如何查询多对多关系

时间:2010-08-03 09:48:32

标签: doctrine dql

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

更新

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

Post 1 -> tag1, tag2
Post 2 -> tag1, tag3
Post 3 -> tag2, tag3

目前我得到的是

Post 1 -> tag2 // missing tag1
Post 3 -> tag2 // missing tag3 

1 个答案:

答案 0 :(得分:4)

假设这些表:

  • 帖子:id,作者,日期,内容
  • 标签:id,name
  • 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'

基本上,将多对多关系视为两个一对多关系,因为这就是它们在普通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')

另一种方法是:

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)

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

现在这会让你回到这样的行:

Post 1, tag 1
Post 1, tag 2
Post 3, tag 2
Post 3, tag 3

因此您需要将它们组合在一起,最好是在应用程序逻辑而不是SQL中。一些RDBMS具有特定于供应商的扩展,例如MySQL的GROUP_CONCAT()功能。