在开始之前,我想指出,“gas”描述的是燃料或一种发动机,而不是一种轿车。在继续这个路径之前,请仔细考虑。(在数据库设计中,语义比大多数人想象的重要。)
你想做的事情相当简单,但并不容易。在这种超类型/子类型设计(也称为独占弧)中,重要的一点是要使有关轿车的行不可能引用有关半卡车等的行。
MySQL使代码更加冗长,因为它不执行CHECK约束。你很幸运;在你的应用程序中,CHECK约束可以被额外的表和外键约束所替代。注释参考上面的SQL。
create table vehicle_types (
veh_type_code char(1) not null,
veh_type_name varchar(10) not null,
primary key (veh_type_code),
unique (veh_type_name)
);
insert into vehicle_types values
('s', 'Semi-truck'), ('c', 'Car');
在其他平台上,我可能会将这种东西作为CHECK约束实现。当代码的含义对用户来说是显而易见时,您可以这样做。我希望用户知道或能够弄清楚“s”代表半挂车,“c”代表轿车,或者视图/应用程序代码将隐藏代码不让用户看到。
create table vehicles (
veh_id integer not null,
veh_type_code char(1) not null,
other_columns char(1) default 'x',
primary key (veh_id),
unique (veh_id, veh_type_code),
foreign key (veh_type_code) references vehicle_types (veh_type_code)
);
UNIQUE约束条件允许列对{veh_id, veh_type_code}成为外键引用的目标。这意味着一个“汽车”行不能错误地引用一个“半挂车”行。
insert into vehicles (veh_id, veh_type_code) values
(1, 's'), (2, 'c'), (3, 'c'), (4, 'c'), (5, 'c'),
(6, 'c'), (7, 'c');
create table car_types (
car_type char(3) not null,
primary key (car_type)
);
insert into car_types values
('Van'), ('SUV'), ('Sed');
create table veh_type_is_car (
veh_type_car char(1) not null,
primary key (veh_type_car)
);
如果在其他平台上,我还会实施作为 CHECK 约束的其他内容(请见下文)。
insert into veh_type_is_car values ('c');
仅有一行。
create table cars (
veh_id integer not null,
veh_type_code char(1) not null default 'c',
car_type char(3) not null,
other_columns char(1) not null default 'x',
primary key (veh_id ),
unique (veh_id, veh_type_code, car_type),
foreign key (veh_id, veh_type_code) references vehicles (veh_id, veh_type_code),
foreign key (car_type) references car_types (car_type),
foreign key (veh_type_code) references veh_type_is_car (veh_type_car)
);
veh_type_code 的默认值,以及对于 veh_type_is_car 的外键引用,保证了此表中的行仅可表示汽车,并且仅能引用汽车。在其他平台上,我会将列 veh_type_code 声明为 veh_type_code char(1) not null default 'c' check (veh_type_code = 'c')
。
insert into cars (veh_id, veh_type_code, car_type) values
(2, 'c', 'Van'), (3, 'c', 'SUV'), (4, 'c', 'Sed'),
(5, 'c', 'Sed'), (6, 'c', 'Sed'), (7, 'c', 'Sed');
create table sedan_types (
sedan_type_code char(1) not null,
primary key (sedan_type_code)
);
insert into sedan_types values
('g'), ('d'), ('h'), ('e');
create table sedans (
veh_id integer not null,
veh_type_code char(1) not null,
car_type char(3) not null,
sedan_type char(1) not null,
other_columns char(1) not null default 'x',
primary key (veh_id),
foreign key (sedan_type) references sedan_types (sedan_type_code),
foreign key (veh_id, veh_type_code, car_type) references cars (veh_id, veh_type_code, car_type)
);
insert into sedans (veh_id, veh_type_code, car_type, sedan_type) values
(4, 'c', 'Sed', 'g'), (5, 'c', 'Sed', 'd'), (6, 'c', 'Sed', 'h'),
(7, 'c', 'Sed', 'e');
如果你需要构建其他引用轿车的表,例如 gas_sedans、diesel_sedans 等,则需要构建类似于“veh_type_is_car”的单行表,并设置对它们的外键引用。
在生产环境中,我会撤销基本表的权限,并使用:
- 可更新视图来执行插入和更新操作;
- 存储过程来执行插入和更新操作。