2

我有一个(简化的)数据库模式,它代表card和。单个可以在多个. 一个有效的对被称为 a 。card_setscard_printscardcard_sets(card, card_set)card_print

我还需要建模关系card_havecard_want.

card_have必须能够表达“我有这张来自特定系列的卡片” 。

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

  1. “我想要这张卡,我不在乎它来自哪个系列。”
  2. “我想要这张卡;我只想要它来自系列”。(或者,考虑这个更简单的变体:“我想要这张卡;我只希望它来自这个系列”。)

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."
  );

它共享解决更难变体的问题。

有没有更好、更优雅的方法来解决这个问题(更难或更简单的变体)?

提前致谢。

4

1 回答 1

2

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

使用代理主键的缺点之一是不关联:
生成的代理键的值与连续保存的数据的实际含义无关。当使用代理键检查包含对另一个表的外键引用的行时,代理键的行的含义无法从键本身中辨别出来。必须连接每个外键才能查看相关数据项。
这也会使审计更加困难,因为不正确的数据并不明显。

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

设计草案将是这样的:

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)
  );
于 2013-11-12T07:57:05.590 回答