【问题标题】:MySQL: Understanding mapping tablesMySQL:了解映射表
【发布时间】:2011-09-21 03:51:04
【问题描述】:

在为具有多对多关系的业务目录构建类别导航系统时,我理解创建映射表是一种很好的做法。

类别表(CategoryId、CategoryName)
业务表(业务 ID、业务名称)
类别映射表(BusinessId、CategoryId)

当我加入 Category 表和 Business 表来创建映射表时,这会给我一个包含所有可能的业务和类别关系的表吗?

我有 800 个类别和 1000 个商家信息。那会不会给我一个包含 800,000 个可能关系的表格。如果是这样,我将如何只关注现有的关系?我是否必须浏览所有列表(800,000),将它们标记为真或假?

我对此感到非常困惑,因此非常感谢任何帮助。

【问题讨论】:

    标签: php mysql database-design mapping categories


    【解决方案1】:

    你只把真正的关系放在映射表中。因此,平均而言,一家企业属于 2 个类别,那么在您的示例中,映射表中只有 2000 条记录,而不是 800,000 条

    “当我加入 Category 表和 Business 表以创建映射表时”您不会加入这两个表来创建映射表。您创建一个实际的物理表。

    【讨论】:

      【解决方案2】:

      当我加入类别表并 创建映射的业务表 这张桌子会给我一张桌子吗 其中包含所有可能的业务 和品类的关系?

      是的。

      我是否必须浏览所有列表 (800,000) 将它们标记为真或假?

      不,您需要使用ON-clause 来设置连接条件。

      SELECT <columns> FROM categories as c 
      INNER JOIN mapping AS m
          ON m.CategoryId = c.CategoryId
      INNER JOIN businesses as b
          ON m.BusinessId = b.BusinessId
      

      【讨论】:

        【解决方案3】:

        当使用多对多关系时,唯一现实的处理方法是使用映射表。

        假设我们的学校有老师和学生,一个学生可以有多个老师,反之亦然。

        所以我们做了3张桌子

        student
          id unsigned integer auto_increment primary key
          name varchar
        
        teacher
          id unsigned integer auto_increment primary key
          name varchar
        
        link_st
          student_id integer not null
          teacher_id integer not null
          primary key (student_id, teacher_id)
        

        学生表将有 1000 条记录
        教师表将有 20 条记录
        link_st 表将包含与链接一样多的记录(不是 20x1000,但仅适用于实际链接)。

        选择
        您选择例如每个教师使用的学生数:

        SELECT s.name, t.name 
        FROM student
        INNER JOIN link_st l ON (l.student_id = s.id)   <--- first link student to the link-table
        INNER JOIN teacher t ON (l.teacher_id = t.id)   <--- then link teacher to the link table.
        ORDER BY t.id, s.id
        

        通常你应该在这里使用inner join

        建立链接
        当您将老师分配给学生时(反之亦然,相同)。 你只需要做:

        INSERT INTO link_st (student_id, teacher_id) 
           SELECT s.id, t.id 
           FROM student s 
           INNER JOIN teacher t ON (t.name = 'Jones')
           WHERE s.name = 'kiddo'
        

        这有点误用内连接,但只要名称是唯一的,它就可以工作。
        如果您知道 id,当然可以直接插入它们。
        如果名称不是唯一的,这将是一个失败,不应使用。

        如何避免重复链接
        避免重复链接非常重要,如果你有这些,就会发生各种不好的事情。
        如果您想防止在链接表中插入重复链接,可以在链接上声明unique 索引(推荐)

        ALTER TABLE link_st
          ADD UNIQUE INDEX s_t (student_id, teacher_id); 
        

        或者您可以在插入语句中进行检查(不是很推荐,但它有效)。

        INSERT INTO link_st (student_id, teacher_id) 
          SELECT s.id, t.id
          FROM student s
          INNER JOIN teacher t ON (t.id = 548)
          LEFT JOIN link_st l ON (l.student_id = s.id AND l.teacher_id = t.id)
          WHERE (s.id = 785) AND (l.id IS NULL)
        

        这只会选择 548、785 如果该数据尚未在 link_st 表中,并且如果该数据已在 link_st 中,则不会返回任何内容。所以它会拒绝插入重复值。

        如果您有一个表学校,这取决于一个学生是否可以在多所学校注册(不太可能,但让我们假设)以及教师是否可以在多所学校注册。很有可能。

        table school
          id unsigned integer auto_increment primary key
          name varchar
        
        table school_members
          id id unsigned integer auto_increment primary key
          school_id integer not null
          member_id integer not null
          is_student boolean not null
        

        您可以像这样列出学校中的所有学生:

        SELECT s.name
        FROM school i
        INNER JOIN school_members m ON (i.id = m.school_id)
        INNER JOIN student s ON (s.id = m.member_id AND m.is_student = true)
        

        【讨论】:

        • 非常感谢 Johan 提供了一个内容丰富且写得很好的答案。我想我明白了。只是一个简单的问题:在 INNER JOIN link_st 之后 l 做了什么?这只是在 ON 子句中使用的缩写吗?
        • 它是大写的 i,而不是小写的 L。它为表格设置别名,因此您可以在不使用表格名称的情况下引用它:l.student_id 而不是 link_st.student_id
        • @phant0m,它是小写的 L,而不是大写的 i,为什么我要为名为 link 的表加上 i 的别名?
        • @Richard,我给表起了别名,因为我很懒,不想输入完整的表名。
        • 哦好吧..那是失败...我只是对所有之间的对比感到非常困惑是,不是,资本不是资本......在这个例子中,我做对了,哈哈----只需将not切换到另一个地方:)
        【解决方案4】:

        当您尝试为多对多或一对多关系建模时,您应该使用映射表。

        例如,在地址簿应用程序中,特定联系人可能属于零个、一个或多个类别。如果您将业务逻辑设置为联系人只能属于一个类别,您可以这样定义您的联系人:

        Contact
        --------------
        contactid (PK)
        name
        categoryid (FK)
        
        Category
        --------------
        categoryid (PK)
        categoryname
        

        但如果您想让一个联系人拥有多个电子邮件地址,请使用映射表:

        Contact
        --------------
        contactid (PK)
        name
        
        Category
        --------------
        categoryid (PK)
        categoryname
        
        Contact_Category
        --------------
        contactid (FK)
        categoryid (FK)
        

        然后您可以使用 SQL 检索联系人分配到的类别列表:

        选择a.categoryname from Category a, Contact b, Contact_Category c where a.categoryid=c.categoryid and b.contactid=c.contactid and b.contactid=12345;

        select a.categoryname 
        from Category a
        inner join Contact_Category c on a.categoryid=c.categoryid
        inner join Contact b on b.contactid=c.contactid
        where b.contactid=12345;
        

        【讨论】:

        • 请避免使用隐式 where 连接。它们令人困惑、容易出错并且不利于您的心理健康。 1989 年将它们埋在它们所属的地方,并改用显式连接。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-08-03
        • 2016-10-20
        • 1970-01-01
        • 2014-06-04
        • 2014-09-16
        • 2011-07-25
        相关资源
        最近更新 更多