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

时间:2012-11-25 02:25:25

标签: database database-design

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

1 个答案:

答案 0 :(得分:7)

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

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

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

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

  1. 我是否只需要向一个方向查询或两者都查询? 1
    • 如果一个方向,只需在两个外键上创建一个复合PRIMARY KEY(让我们称之为PARENT_ID和CHILD_ID)。订单很重要:如果您从父母查询到子女,PK应为:{PARENT_ID,CHILD_ID}。
    • 如果双向,也会以相反的顺序创建一个复合索引,在这种情况下为{CHILD_ID,PARENT_ID}。
  2. “额外”数据是否很小?
    • 如果cluster表格,cover必要时辅助索引中的额外数据。 2
    • 没有,不要对表进行聚类,也不要覆盖二级索引中的额外数据。 3
  3. 是否有任何其他表,联结表作为父表?
    • 如果,请考虑添加代理键是否值得保持儿童FK苗条。但请注意,如果添加代理键,这可能会消除群集的机会。
  4. 在许多情况下,这些问题的答案将是: both,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支持leading-edge index compression。由于聚簇表本质上是一个索引,因此也可以对其应用压缩。
    • JUNCTION_TABLE_IE1EXTRA_DATA:由于辅助索引覆盖了额外的数据,因此DBMS可以在查询从子级到父级的方向时不触及表格。主键充当群集键,因此在从父项查询子项时,自然会覆盖额外数据。

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

    以下是等效的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 覆盖允许仅从索引中满足查询,并避免在通过群集表中的二级索引访问数据时需要的昂贵的双重查找。 / em>的

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