我有一个(简化的)数据库架构,代表card
,card_sets
和card_prints
。
单个card
可以在多个card_sets
中展示。有效的(card, card_set)
对称为card_print
。
我还需要对关系card_have
和card_want
进行建模。
card_have
必须能够表达“我从特定集中获取此卡。”
card_want
必须能够表达以下两种情况:
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_id
与card_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."
);
它与更难的变体解决了问题。
是否有更好,更优雅的方法来解决这个问题(更难或更容易的变体)?
提前致谢。
答案 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)
);