Если вам нужны только 2 или 3 категории (растения / метазоа / бактерии) и вы хотите смоделировать отношения XOR, возможно, «дуга» - это решение для вас. Преимущество: нет необходимости в триггерах. Примеры диаграмм можно найти [здесь] [1]. В вашей ситуации таблица «контейнеры» будет иметь 3 столбца с ограничением CHECK, что позволяет использовать либо растение, либо животное, либо бактерию.
Это, вероятно, не подходит, если в будущем будет необходимо различать множество категорий (например, роды, виды, подвиды). Однако для 2-3 групп / категорий это может помочь.
ОБНОВЛЕНИЕ: Вдохновленное предложениями и комментариями участника, другое решение, которое допускает множество таксонов (групп родственных организмов, классифицированных биологом) и избегает «определенных» имен таблиц (PostgreSQL 9.5).
Код DDL:
-- containers: may have more columns eg for temperature, humidity etc
create table containers (
ctr_name varchar(64) unique
);
-- taxonomy - have as many taxa as needed (not just plants/animals/bacteria)
create table taxa (
t_name varchar(64) unique
);
create table organisms (
o_id integer primary key
, o_name varchar(64)
, t_name varchar(64) references taxa(t_name)
, unique (o_id, t_name)
);
-- table for mapping containers to organisms and (their) taxon,
-- each container contains organisms of one and the same taxon
create table collection (
ctr_name varchar(64) references containers(ctr_name)
, o_id integer
, t_name varchar(64)
, unique (ctr_name, o_id)
);
-- exclude : taxa that are different from those already in a container
alter table collection
add exclude using gist (ctr_name with =, t_name with <>);
-- FK : is the o_id <-> t_name (organism-taxon) mapping correct?
alter table collection
add constraint taxon_fkey
foreign key (o_id, t_name) references organisms (o_id, t_name) ;
Тестовые данные:
insert into containers values ('container_a'),('container_b'),('container_c');
insert into taxa values('t:plant'),('t:animal'),('t:bacterium');
insert into organisms values
(1, 'p1', 't:plant'),(2, 'p2', 't:plant'),(3, 'p3', 't:plant'),
(11, 'a1', 't:animal'),(22, 'a1', 't:animal'),(33, 'a1', 't:animal'),
(111, 'b1', 't:bacterium'),(222, 'b1', 't:bacterium'),(333, 'b1', 't:bacterium');
Тестирование:
-- several plants can be in one and the same container (3 inserts succeed)
insert into collection values ('container_a', 1, 't:plant');
insert into collection values ('container_a', 2, 't:plant');
insert into collection values ('container_a', 3, 't:plant');
-- 3 inserts that fail:
-- organism id in a container must be UNIQUE
insert into collection values ('container_a', 1, 't:plant');
-- bacteria not allowed in container_a, populated by plants (EXCLUSION at work)
insert into collection values ('container_a', 333, 't:bacterium');
-- organism with id 333 is NOT a plant -> insert prevented by FK
insert into collection values ('container_a', 333, 't:plant');
Спасибо @RDFozz и @Evan Carroll и @ypercube за их вклад и терпение (чтение / исправление моих ответов).