设计RDBMS架构。代表一个子集

时间:2013-11-11 19:00:27

标签: database-design relational-database

我有一个(简化的)数据库架构,代表cardcard_setscard_prints。 单个card可以在多个card_sets中展示。有效的(card, card_set)对称为card_print

我还需要对关系card_havecard_want进行建模。

card_have必须能够表达“我从特定集中获取此卡。”

card_want必须能够表达以下两种情况:

  1. “我想要这张卡片;我不关心它的来源。”
  2. “我想要这张卡片;我只希望它来自套装”。 (或者,考虑一下这个更容易的变体:“我想要这张卡片;我只希望它来自这个套装”。)
  3. card_want归结为代表card_prints的可能card的子集。

    这是我到目前为止(稍微简化):

    CREATE TABLE IF NOT EXISTS "card"
      ( id    BIGSERIAL  NOT NULL  UNIQUE  PRIMARY KEY
      , name  TEXT       NOT NULL  UNIQUE
      );
    
    CREATE TABLE IF NOT EXISTS "card_set"
      ( id    BIGSERIAL  NOT NULL  UNIQUE  PRIMARY KEY
      , name  TEXT       NOT NULL  UNIQUE
      );
    
    CREATE TABLE IF NOT EXISTS "card_print"
      ( id           BIGSERIAL  NOT NULL  UNIQUE  PRIMARY KEY
      , card_id      BIGINT     NOT NULL  REFERENCES "card"(id)
      , card_set_id  BIGINT     NOT NULL  REFERENCES "card_set"(id)
      );
    
    CREATE TABLE IF NOT EXISTS "card_have"
      ( id             BIGSERIAL  NOT NULL  UNIQUE  PRIMARY KEY
      , card_print_id  BIGINT     NOT NULL  REFERENCES "card_print"(id)
      );
    
    CREATE TABLE IF NOT EXISTS "card_want"
      ( id       BIGSERIAL  NOT NULL  UNIQUE  PRIMARY KEY
      );
    
    CREATE TABLE IF NOT EXISTS "card_want_set"
      ( card_want_id    BIGINT     NOT NULL  REFERENCES "card_want"(id)
      -- One the following two lines:
      -- , card_print_id  BIGINT  NOT NULL  REFERENCES "card_print"(id) 
      -- , card_set_id  BIGINT  NOT NULL  REFERENCES "card_print"(id) 
      );
    

    “我不关心卡片的来源”将表示不将任何card_print_id / card_set_idcard_want实例相关联。

    这些解决方案的问题在于,关联的card_print_id / card_set_id可能与card_id不对应。通过在card_print_id / card_set_id上引入约束可以稍微减轻这一点,但这似乎仍然有点不优雅和次优。

    更容易变体的解决方案可能如下所示:

    CREATE TABLE IF NOT EXISTS "card_want"
      ( id       BIGSERIAL  NOT NULL  UNIQUE  PRIMARY KEY
      , card_id  BIGINT     NOT NULL  REFERENCES "card_print"(id)
      , card_set_id  BIGINT  REFERENCES "card_set"(id) -- NULL means "I do not care about the set."
      );
    

    它与更难的变体解决了问题。

    是否有更好,更优雅的方法来解决这个问题(更难或更容易的变体)?

    提前致谢。

1 个答案:

答案 0 :(得分:2)

我认为问题来源是您使用代理主键(id列)设计了连接表。

使用代理主键的一个缺点是disassociation:
生成的代理键的值与连续保存的数据的真实含义无关。当使用代理键检查持有外键引用到另一个表的行时,无法从键本身识别代理键行的含义。必须连接每个外键才能查看相关数据项。
这也会使审核变得更加困难,因为不正确的数据并不明显。

所以我认为如果您使用非代理键设计表格并在必要时使用唯一键,问题就可以解决了。

设计草案如下:

CREATE TABLE IF NOT EXISTS "card"
  ( id    BIGSERIAL  NOT NULL  UNIQUE  PRIMARY KEY
  , name  TEXT       NOT NULL  UNIQUE
  );

CREATE TABLE IF NOT EXISTS "card_set"
  ( id    BIGSERIAL  NOT NULL  UNIQUE  PRIMARY KEY
  , name  TEXT       NOT NULL  UNIQUE
  );

CREATE TABLE IF NOT EXISTS "card_print"  -- (card_id + card_set_id) are the PRIMARY key
  ( card_id      BIGINT     NOT NULL  REFERENCES "card"(id) PRIMARY KEY
  , card_set_id  BIGINT     NOT NULL  REFERENCES "card_set"(id) PRIMARY KEY
  );

CREATE TABLE IF NOT EXISTS "card_have" -- card_set_id + card_id is the  PRIMARY KEY
  ( card_id      BIGINT     NOT NULL  REFERENCES "card_print"(card_id) PRIMARY KEY
  , card_set_id  BIGINT     NOT NULL  REFERENCES "card_print"(card_set_id) PRIMARY KEY      
  );

CREATE TABLE IF NOT EXISTS "card_want" -- card_id  is the PRIMARY KEY 
  ( card_id  BIGSERIAL  NOT NULL  UNIQUE  PRIMARY KEY REFERENCES "card(id)"
  );

CREATE TABLE IF NOT EXISTS "card_want_set" (card_id  + card_set_id) is the PRIMARY KEY 
  ( card_id  BIGSERIAL  NOT NULL  PRIMARY KEY REFERENCES "card_print(card_id)"
    ,card_set_id  BIGINT     NOT NULL PRIMARY REFERENCES "card_print"(card_set_id)
  );