如何模拟多对多的关系?

时间:2009-07-24 06:22:08

标签: php sql database many-to-many

由于以前只有一些数据库经验并且没有接受过正式教育,我对如何建模(以及在PHP中检索我需要的数据)感到有些困惑。这就是我想要建模的地方:

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

我一直在努力阅读如何做到这一点,以及缺乏经验或其他我不知道的事情,但我似乎无法理解这个概念。显然你需要一个将两者连在一起的中间表?

此外,一旦我建立了这种关系并定义了表格,我将如何做以下事情: - 检索具有特定标签的所有商品? - 检索一个项目的所有标签?

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

4 个答案:

答案 0 :(得分:7)

db部分很简单。这只是一个示例,因此您可以看到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'
        )
    );
}

答案 1 :(得分:5)

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

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

然后,为了查询数据,您应该在select语句中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'

答案 2 :(得分: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

答案 3 :(得分:0)

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

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

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