如何建模多对多关系?

3

我之前只有一点数据库的经验,没有正式的学习过。现在我遇到了一个问题,不知道如何对其进行建模(并在PHP中检索所需数据)。这就是我要建模的内容:

对于我网站上的每个项目,允许有多个标签,例如文件、上传、php、递归等等。但是这些标签是可重复使用的,我可以有两个不同的项目,它们都可以有php标签。

我一直在尝试阅读如何做到这一点,无论是缺乏经验还是其他原因,我都无法掌握这个概念。显然,你需要一个中间表来将两者联系起来?

此外,一旦我有了这种关系和定义好的表,我该如何做以下事情: - 检索具有特定标签的所有项目? - 检索一个项目拥有的所有标签?

感谢您的帮助,如果有人能列出任何更深入的阅读材料以加强我对这个概念的理解,那将是非常棒的。

4个回答

7

数据库部分很简单。这只是一个示例,让您了解数据库可能的样子,并不是任何特定的SQL引擎查询。

CREATE TABLE posts (
    id INT PRIMARY KEY,
    subject VARCHAR(100),
    body TEXT
)

CREATE TABLE tags (
    id INT PRIMARY KEY,
    name VARCHAR(50)
)

CREATE TABLE post_tags (
    post_id INT,
    tag_id INT,
    FOREIGN KEY (post_id) REFERENCES posts (id),
    FOREIGN KEY (tag_id) REFERENCES posts (id)
)

为了获取带有“yourTag”标签的项,您只需要运行以下查询:
SELECT P.*
FROM posts P 
    LEFT JOIN post_tags PT ON (PT.post_id = P.id)
    LEFT JOIN tags T ON (T.id = PT.tag_id)
WHERE T.name = 'yourTag';

要获取与帖子ID为123相关联的标签,您可以运行此查询:

SELECT T.*
FROM tags T 
    LEFT JOIN post_tags PT ON (T.id = PT.tag_id)
    LEFT JOIN posts P ON (PT.post_id = P.id)
WHERE P.id = 123;

对于 PHP 部分,您可以使用框架。许多(如果不是全部)框架都可以轻松地建模这种关系。例如,在 CakePHP 中,可以这样完成:

class Post extends AppModel {
    $useTable = 'posts';
    $hasAndBelongsToMany = array(
        'Tag' => array(
            'className' => 'Tag'
            'joinTable' => 'post_tags'
            'foreignKey' => 'post_id'
            'associationForeignKey' => 'tag_id'
        )
    );
}

class Tag extends AppModel {
    $useTable = 'tags';
    $hasAndBelongsToMany = array(
        'Post' => array(
            'className' => 'Post'
            'joinTable' => 'post_tags'
            'foreignKey' => 'tag_id'
            'associationForeignKey' => 'post_id'
        )
    );
}

ITYM标签->$hasAndBelongsToMany = array('Post' ...等。 - Stobor
感谢提供数据库部分,太棒了。我使用CodeIgniter,它实际上不会像那样建模关系,你必须自己完成。此外,我更愿意真正理解涉及的SQL查询,而不是依赖于自动化。 - Zim
@James - 我也为你准备了一个示例查询。理解查询语句很重要,但一旦开始使用ORM,你可能就不会再回到手动输入SQL语句了。 - RaYell
展示得很好。比我经常看到的将数组序列化然后放入表格单元格中要好一些的解决方案。 - Kenny Michaels

5

你应该使用一个中间表来关联这两个实体:

--------     1:n    ------------          --------- 
| ITEM |-¦---------<| ITEM_TAG |   n:1    |  TAG  |
| Id   |            | ItemId   |>-------¦-| Id    |
| Name |            | TagId    |          | Name  |
¯¯¯¯¯¯¯¯            ¯¯¯¯¯¯¯¯¯¯¯¯          ¯¯¯¯¯¯¯¯¯

然后,为了查询数据,你应该在选择语句中连接你的表:

标签为“FooTag”的所有项目

SELECT item.* FROM item 
              JOIN item_tag on item.id = item_tag.itemId
              JOIN tag on item_tag.tagId = tag.id
WHERE tag.Name = 'FooTag'

名称为 "FooItem" 的项目的所有标签

SELECT tag.* FROM tag 
             JOIN item_tag on tag.id = item_tag.tagId
             JOIN item on item_tag.itemId = item.id
WHERE item.Name = 'FooItem'

0

你说得对,多对多关系可以使用附加表来实现,例如:

Blog_entry(entry_id, entry_body)
Tag(tag_id, tag_name)
Entry_tag(entry_id, tag_id)

任何操作都是使用多个连接完成的。例如,如果您想选择所有带有标签“foo”的条目,并使用我的示例表格,则必须执行以下操作:

select * 
from
    blog_entry, tag, entry_tag
where
    tag.tag_name = 'foo' and
    entry_tag.tag_id = tag.tag_id and
    entry_tag.entry_id = blog_entry.entry_id

(更新)要检索某个条目(此处为ID 123)具有的所有标签:

select tag_name
from
    blog_entry, tag, entry_tag
where
    Blog_entry.entry_id = 123
    entry_tag.tag_id = tag.tag_id and
    entry_tag.entry_id = blog_entry.entry_id

0

是的,多对多关系需要额外的第三个表,称为关联表。

数据库部分并不难,更难的是在代码中使用它,因为所有这些左连接可能会变得非常混乱 :)

我的建议是使用ORM框架,例如Doctrine或Propel(尽管我更喜欢Doctrine),它们甚至可以为您处理一些复杂的查询。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接