我们如何处理快速增长的交叉表?

Jai*_*gus 3 database database-design

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

Bra*_*vic 7

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

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

  • 高效的查询(通过索引和聚类).
  • 执行参照完整性.

设计联结表时,请询问以下问题:

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

在许多情况下,这些问题的答案将是:yes,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;
Run Code Online (Sandbox Code Playgroud)

注意事项:

  • ORGANIZATION INDEX:针对大多数DBMS调用群集的特定于Oracle的语法.其他DBMS有自己的语法,一些(MySQL/InnoDB)暗示集群,用户无法将其关闭.
  • COMPRESS:某些DBMS支持前沿索引压缩.由于聚簇表本质上是一个索引,因此也可以对其应用压缩.
  • JUNCTION_TABLE_IE1,EXTRA_DATA:由于辅助索引覆盖了额外的数据,因此在从子级到父级的方向查询时,DBMS可以在不触及表的情况下获取它.主键充当群集键,因此在从父级查询子级时,自然会覆盖额外数据.

在物理上,您只有两个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);
Run Code Online (Sandbox Code Playgroud)

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


1换句话说,你只需要得到"父母"的"孩子",或者你可能还需要得到给定孩子的父母.

2覆盖允许仅从索引中满足查询,并避免在通过群集表中的二级索引访问数据时必需的昂贵的双重查找.

3这样,额外的数据不会重复(这会很昂贵,因为它很大),但是你避免了双重查找并用(更便宜的)表堆访问来替换它.但是,要注意可能破坏基于堆的表中范围扫描性能的聚簇因子!