重要的想法是,组织和个人并不完全相同,但它们并不完全不同。两者都可以有姓名、地址和电话号码,并且显然都可以参加会议。
这种东西有各种搜索词,包括“独占弧”和“超类型/子类型”。我为 PostgreSQL 编写了这个最小的示例,但它主要是标准 SQL。 (自动 id 编号和触发器的语法因 dbms 而异。)
表“party”是超类型。表“inds”(个人)和“orgs”(组织)是子类型。每一方都必须是个人或组织;一方不能两者兼得,即使是错误的。 (这真的很重要。)
-- The "supertype". Attributes that apply to both individuals and to
-- organizations go in this table.
--
create table parties (
party_id serial primary key,
party_type char(1) check (party_type in ('I', 'O')),
party_full_name varchar(45) not null,
-- This unique constraint lets foreign keys reference this pair
-- of columns.
unique (party_id, party_type)
);
-- For organizations, a "subtype" of parties. There's nothing special about
-- the column "ein". It's just an attribute that applies to organizations,
-- but doesn't apply to individuals.
--
create table orgs (
party_id integer primary key,
party_type CHAR(1) not null default 'O' check (party_type = 'O'),
ein CHAR(10), -- In the USA, federal Employer Identification Number
-- This reference to a pair of columns, together with the CHECK
-- constraint above, guarantees that a row in this table will
-- reference an organization in "parties". It's impossible for
-- a row in this table to reference an individual.
--
foreign key (party_id, party_type)
references parties (party_id, party_type) on delete cascade
);
-- For individuals, a "subtype" of parties. There's nothing special about
-- the column "height_in" (height in inches). It's just an attribute that
-- applies to individuals, but doesn't apply to organizations.
--
create table inds (
party_id integer primary key,
party_type char(1) not null default 'I' check (party_type = 'I'),
height_in integer not null check (height_in between 24 and 108),
-- See comments in "orgs" above.
foreign key (party_id, party_type)
references parties (party_id, party_type) on delete cascade
);
客户端代码使用可更新的视图,而不是基表。平台对可更新视图的支持各不相同。大多数允许在视图上触发,这是我在下面使用的。
我为下面的人写了一个视图。触发器仅处理插入。更新和删除的代码非常相似。组织的查看和支持代码也类似。
create view people as
select t1.party_id, t1.party_full_name, t2.height_in
from parties t1
inner join inds t2 on (t1.party_id = t2.party_id);
create or replace function insert_into_people()
returns trigger as
$$
begin
insert into parties (party_full_name, party_type)
values (new.party_full_name, 'I');
insert into inds (party_id, height_in) values (currval('parties_party_id_seq'), new.height_in);
return null;
end;
$$
language plpgsql;
create trigger insert_people
instead of insert on people
for each row
execute procedure insert_into_people();
在生产中,您可能会撤消几乎所有用户对基表的权限,并且只允许通过视图进行访问。 (您可能需要更多浏览量。)
由于个人和组织都可以参加会议,因此您的会议参与者表将引用“party”.“party_id”。