Eu tive alguns problemas ao modelar um esquema elétrico no SQL. A estrutura que eu gostaria de capturar é
part ←────────── pin
↑ ↑
part_inst ←───── pin_inst
onde "inst" é a abreviação de "instance".
Por exemplo, eu posso ter como um part
amplificador operacional LM358 com pin
s 1OUT, 1IN-, 1IN +, GND, 2IN +, 2IN-, 2OUT e V CC . Eu poderia então colocar esta parte em um esquema, criando a part_inst
e 8
pin_inst
s.
Ignorando os campos de dados, minha tentativa inicial de um esquema foi
create table parts (
part_id bigserial primary key
);
create table pins (
pin_id bigserial primary key,
part_id bigint not null references parts
);
create table part_insts (
part_inst_id bigserial primary key,
part_id bigint not null references parts
);
create table pin_insts (
pin_inst_id bigserial primary key,
part_inst_id bigint not null references part_insts,
pin_id bigint not null references pins
);
O principal problema com esse esquema é que um pin_inst
pode estar vinculado a um part_inst
com part_id=1
mas pin
tem part_id=2
.
Eu gostaria de evitar esse problema no nível do banco de dados e não no nível do aplicativo. Então, eu modifiquei minhas chaves primárias para impor isso. Marquei as linhas alteradas com --
.
create table parts (
part_id bigserial primary key
);
create table pins (
pin_id bigserial, --
part_id bigint not null references parts,
primary key (pin_id, part_id) --
);
create table part_insts (
part_inst_id bigserial, --
part_id bigint not null references parts,
primary key (part_inst_id, part_id) --
);
create table pin_insts (
pin_inst_id bigserial primary key,
part_inst_id bigint not null, --
pin_id bigint not null, --
part_id bigint not null references parts, --
foreign key (part_inst_id, part_id) references part_insts, --
foreign key (pin_id, part_id) references pins --
);
Minha queixa com esse método é que polui as chaves primárias: em todos os lugares em que me refiro a part_inst
, preciso acompanhar tanto o
part_inst_id
quanto o part_id
. Existe outra maneira de impor a restrição
pin_inst.part_inst.part_id = pin_inst.pin.part_id
sem ser excessivamente detalhado?
fonte
pin_inst_id
que é uma redundância. Você pode usar a(part_inst_id, part_id, pin_id)
chave primária.Respostas:
Solução mínima
Uma solução radical pode ser remover
pin_inst
completamente:Não há nada na sua pergunta que sugira que você realmente precise da tabela redundante. Para
pin
s associado a apart_inst
, observe ospin
s do associadopart
.Isso simplificaria o código para:
Mas seu comentário deixou claro que não vamos nos safar disso ...
Alternativa se
pin_inst
for necessárioIncluir
part_id
como você fez é a solução mais simples com restrições de chave estrangeira. Você não pode fazer referência a uma tabela "a duas tabelas de distância" com restrições de chave estrangeira .Mas você pode pelo menos se contentar sem "poluir" as chaves primárias. Adicione
UNIQUE
restrições .Coloquei
part_id
primeiro as restrições únicas. Isso é irrelevante para a integridade referencial, mas é importante para o desempenho. As chaves primárias já implementam índices para as colunas pk. É melhor ter a outra coluna primeiro nos índices de várias colunas implementando as restrições exclusivas. Detalhes sobre estas perguntas relacionadas:Perguntas relacionadas ao SO:
Alternativa com gatilhos
Você pode recorrer a funções de gatilhos, que são mais flexíveis, mas um pouco mais complicadas, propensas a erros e um pouco menos rigorosas. O benefício: você poderia prescindir
part_inst.part_id
epin.part_id
...fonte
pin_insts
, mas eu as omiti no interesse da legibilidade ("Ignorando campos de dados, [...]"). Por exemplo, apin_inst
pode ser marcado como uma entrada ou saída.