如何建模多对多关系?

Posted

技术标签:

【中文标题】如何建模多对多关系?【英文标题】:How to model a many to many relationship? 【发布时间】:2009-07-24 06:22:08 【问题描述】:

以前只有一点数据库经验并且没有接受过正规教育,我对如何建模(并在 php 中从中检索我需要的数据)有点困惑。这就是我要建模的内容:

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

我一直在尝试了解如何执行此操作,以及是否缺乏经验或其他我不知道的原因,但我似乎无法掌握这个概念。显然您需要一个将两者联系在一起的中间表?

此外,一旦我有了这种关系并定义了表,我将如何执行以下操作: - 检索具有特定标签的所有项目? - 检索一个项目的所有标签?

感谢您的帮助,如果有人可以列出任何对此的进一步阅读,以加强我对这个概念的理解,那就太好了。

【问题讨论】:

【参考方案1】:

数据库部分很简单。这只是一个示例,因此您可以查看 db 的外观,而不是任何特定的 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' ...等 感谢数据库部分太棒了。我使用 CodeIgniter,它实际上并没有像那样建模关系,你必须自己做。另外,我更愿意真正了解所涉及的 SQL 查询,而不是依赖 automagic。 @James - 我也为您提供了一个示例查询。理解查询很重要,但是一旦你开始使用 ORM,你可能就不会再回到输入 SQL 了 很好地展示了。比序列化数组然后像我经常看到的那样将它们放入表格单元格中更好的解决方案。【参考方案2】:

您应该使用中间表来关联两个实体:

-------- 1:n ------------ --------- | 项目 |-¦---------ITEM_TAG | n:1 | 标签 | |身份证 | |项目ID |>--------¦-|身份证 | |姓名 | |标签 ID | |姓名 | ¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯

然后查询数据,你应该在选择语句中join你的表:

标签“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'

【讨论】:

【参考方案3】:

你是对的,多对多关系是使用附加表实现的,例如:

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

【讨论】:

【参考方案4】:

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

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

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

【讨论】:

以上是关于如何建模多对多关系?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 DynamoDB 中建模一对一、一对多和多对多关系

DW中尺寸之间的多对多关系-更好的建模?

在关系数据库中对相同实体之间的多个多对多关系进行建模

建模数据仓库中的多对多关系

《Entity Framework 6 Recipes》翻译系列 -----第二章 实体数据建模基础之有载荷和无载荷的多对多关系建模 (转)

在 Django 中建模异构多对多关系的最佳方法是啥?