4

例如,我们有表 A 和表 B,它们具有多对多关系。一个交集表,表 C 存储 A.id 和 B.id 以及表示两者之间关系的值。或者作为一个具体的例子,想象有一个用户帐户、一个论坛和一个业力分数的 stackexchange。或者,学生、课程和成绩。如果表 A 和 B 非常大,表 C 可以并且很可能会非常快速地增长(实际上我们假设它确实如此)。我们该如何处理这样的问题?有没有更好的方法来设计表格以避免这种情况?

4

1 回答 1

9

没有魔法。如果某些行是连接的,而有些行没有连接,则必须以某种方式表示此信息,并且执行此操作的“关系”方式是“连接”(也称为“链接”)表。是的,联结表可以变大,但幸运的是数据库非常有能力处理大量数据。

使用联结表而不是逗号分隔列表(或类似列表)有充分的理由,包括:

  • 高效查询(通过索引和集群)。
  • 执行参照完整性。

在设计连接表时,请提出以下问题:

  1. 我需要只向一个方向查询还是同时向两个方向查询?1
    • 如果是一个方向,只需在两个外键上创建一个复合主键(我们称它们为 PARENT_ID 和 CHILD_ID)。顺序很重要:如果您从父级查询到子级,PK 应该是:{PARENT_ID, CHILD_ID}。
    • 如果两个方向,也以相反的顺序创建一个复合索引,在这种情况下是 {CHILD_ID, PARENT_ID}。
  2. “额外”的数据很小吗?
    • 如果,则对表进行聚类并根据需要覆盖二级索引中的额外数据。2
    • ,不要聚集表,也不要覆盖二级索引中的额外数据。3
  3. 是否有任何其他表可以将联结表用作父表?
    • 如果,请考虑添加代理键是否值得让子 FK 保持苗条。但请注意,如果您添加代理键,这可能会消除集群的机会。

在许多情况下,这些问题的答案是:yes 和 no,在这种情况下,您的表将与此类似(Oracle 语法如下):

CREATE TABLE JUNCTION_TABLE (
    PARENT_ID INT,
    CHILD_ID INT,
    EXTRA_DATA VARCHAR2(50),
    PRIMARY KEY (PARENT_ID, CHILD_ID),
    FOREIGN KEY (PARENT_ID) REFERENCES PARENT_TABLE (PARENT_ID),
    FOREIGN KEY (CHILD_ID) REFERENCES CHILD_TABLE (CHILD_ID)
) ORGANIZATION INDEX COMPRESS;

CREATE UNIQUE INDEX JUNCTION_TABLE_IE1 ON
    JUNCTION_TABLE (CHILD_ID, PARENT_ID, EXTRA_DATA) COMPRESS;

注意事项:

  • ORGANIZATION INDEX:大多数 DBMS 称为集群的特定于 Oracle 的语法。其他 DBMS 有自己的语法,有些(MySQL/InnoDB)暗示集群,用户不能关闭它。
  • COMPRESS: 一些 DBMS 支持领先的索引压缩。由于聚集表本质上是一个索引,因此也可以对其应用压缩。
  • JUNCTION_TABLE_IE1, EXTRA_DATA: 由于二级索引覆盖了多余的数据,所以DBMS在从child到parent的方向查询时,不用碰表就可以得到。主键充当聚类键,因此在从父级向子级查询时自然会覆盖额外的数据。

从物理上讲,您只有两棵 B 树(一棵是聚簇表,另一棵是二级索引),根本没有表堆。这转化为良好的查询性能(父到子和子到父方向都可以通过简单的索引范围扫描来满足)和插入/删除行时相当小的开销。

这是等效的 MS SQL Server 语法(无索引压缩):

CREATE TABLE JUNCTION_TABLE (
    PARENT_ID INT,
    CHILD_ID INT,
    EXTRA_DATA VARCHAR(50),
    PRIMARY KEY (PARENT_ID, CHILD_ID),
    FOREIGN KEY (PARENT_ID) REFERENCES PARENT_TABLE (PARENT_ID),
    FOREIGN KEY (CHILD_ID) REFERENCES CHILD_TABLE (CHILD_ID)
);

CREATE UNIQUE INDEX JUNCTION_TABLE_IE1 ON
    JUNCTION_TABLE (CHILD_ID, PARENT_ID) INCLUDE (EXTRA_DATA);

请注意,除非指定 PRIMARY KEY NONCLUSTERED ,否则 MS SQL Server 会自动对表进行聚类。


1换句话说,您是否只需要获取给定“父母”的“孩子”,或者您可能需要获取给定孩子的父母。

2覆盖允许仅从索引满足查询,并避免在通过聚簇表中的二级索引访问数据时需要进行昂贵的双重查找。

3这样,额外的数据不会重复(这会很昂贵,因为它很大),但是您避免了双重查找并将其替换为(更便宜的)表堆访问。但是,请注意可能会破坏基于堆的表中范围扫描的性能的集群因素!

于 2012-11-25T08:13:16.563 回答