在我看来,您的设计的核心问题是代理键的使用。
不必总是为表创建数字单列键。
即使你这样做了,这也不能保证你不会有重复。
实际上,这会强制系统在您的表上保留更多索引,即
一项额外的工作要做。
-
一些概念:
- 我使用单数作为表名,'因为表中的每个元组代表
关系的一个对象;
- 我使用小写字母作为表名和列名(标识符)和
关键字的大写字母。我不喜欢数据库中的 CamelCase;
- 我将
<table_name>_id 用于PK 列。也有可能是单数
表名;
- 我对我创建的所有约束和索引使用前缀 + 表名 + 详细信息。
您不必遵循它们,但坚持一致的命名会非常好
设计模式。
-
我会从 property_type 字典开始:
CREATE TABLE property_type (
property_type varchar(20) NOT NULL,
CONSTRAINT p_property_type PRIMARY KEY (property_type)
);
这是一个单列表,它的存在只是为了提供一个可能的域
属性类型的值。我用了varchar(20),文字栏很漂亮
美好的。这样做的好处——您不必通过数字连接回该表
获取property_type_id=123 含义的密钥。
-
属性:
CREATE TABLE property (
property_id integer NOT NULL,
property_name varchar(50) NOT NULL,
property_type varchar(20) NOT NULL,
CONSTRAINT p_property PRIMARY KEY (property_id),
CONSTRAINT u_property_name UNIQUE (property_name),
CONSTRAINT f_property_type FOREIGN KEY (property_type)
REFERENCES property_type ON UPDATE CASCADE
);
我决定在这里使用数字 PK,因为我想您可能想要重命名属性
在某一点。如果您更改property_type,更新将被级联。
尽管这里已经有一个 PK,但 UNIQUE 对名称的约束在这里是必须的,
否则,您可能会遇到具有相同名称属性的情况
具有不同的 ID。
-
DT 表:
CREATE TABLE dt (
dt_id integer NOT NULL,
dt_name varchar(50) NOT NULL,
CONSTRAINT p_dt PRIMARY KEY (dt_id),
CONSTRAINT u_dt_name UNIQUE (dt_name)
);
同样,只有 PK 是不够的,还要创建 UNIQUE 约束。
虽然我非常想完全摆脱dt_id 并且只会保留
dt_name 本来可以PK的。
-
DT 的属性:
CREATE TABLE dt_property (
dt_id integer NOT NULL,
property_id integer NOT NULL,
initial_value varchar(50) NOT NULL,
CONSTRAINT p_dt_property PRIMARY KEY (dt_id, property_id),
CONSTRAINT f_dt_id FOREIGN KEY (dt_id) REFERENCES dt,
CONSTRAINT f_property_id FOREIGN KEY (property_id) REFERENCES property
);
这是与您的设计的第一个重大区别——使用了复合键。
是的,这意味着您必须随时执行 2 列
请参阅此表中的条目。但这没什么大不了的,真的——你
设计表一次,您也编写一次查询,但您的软件
如果操作得当且易于维护,可能会使用相当长的一段时间。
最好花更多时间编写查询并轻松维护
长期系统。
-
标签:
CREATE TABLE tag (
tag_id integer NOT NULL,
tag_name varchar(50) NOT NULL,
CONSTRAINT p_tag PRIMARY KEY (tag_id),
CONSTRAINT u_tag_name UNIQUE (tag_name)
);
这只是另一本字典。同样,就像dt 表一样,我真的很想要
避免使用tag_id 列,只保留tag_name,使其也成为PK。
-
新表tag_dt介绍:
CREATE TABLE tag_dt (
tag_id integer NOT NULL,
dt_id integer NOT NULL,
CONSTRAINT p_tag_dt PRIMARY KEY (tag_id, dt_id),
CONSTRAINT f_tag_id FOREIGN KEY (tag_id) REFERENCES tag,
CONSTRAINT f_dt_id FOREIGN KEY (dt_id) REFERENCES dt
);
创建dt + tag 关系需要此表。没有它,你就有数据
重复 - 你可以在你的架构上看到它,你有 2 行 Tag_name='Tag1'。
-
最后,标签属性:
CREATE TABLE tag_property (
tag_id integer NOT NULL,
dt_id integer NOT NULL,
property_id integer NOT NULL,
a_value varchar(50) NOT NULL,
CONSTRAINT p_tag_property PRIMARY KEY (tag_id, dt_id),
CONSTRAINT u_tag_property UNIQUE (tag_id, property_id),
CONSTRAINT f_tag_property_tag FOREIGN KEY (tag_id, dt_id) REFERENCES tag_dt,
CONSTRAINT f_tag_property_property FOREIGN KEY (dt_id, property_id)
REFERENCES dt_property
);
此表是完整的复合键,它符合您的所有要求。
主键是tag_id, dt_id,它也是tag_dt表的外键,
所以你希望能够引入一些以前没有定义的东西。
接下来,tag_id, property_id 是唯一的,这意味着标签的属性不能重复。
最后,dt_id, property_id 引用了dt_property 表,这意味着
只有dt 允许的属性才会被注册。
在大多数 DBMS 中,所有主键和唯一键都是通过索引实现的。此外,一些 DBMS
即使省略了键的第一列,也可以使用复合索引(多列索引)。
至少PostgreSQL可以做到,这是我用得最多的。