Обеспечение соблюдения ограничений «на расстоянии двух столов»

10

Я столкнулся с некоторыми проблемами при моделировании электрической схемы в SQL. Структура, которую я хотел бы захватить,

  part ←────────── pin
                   
part_inst ←───── pin_inst

где «inst» - это сокращение от «instance».

Например, я мог бы иметь в partкачестве операционного усилителя LM358 с pins 1OUT, 1IN-, 1IN +, GND, 2IN +, 2IN-, 2OUT и V CC . Затем я мог бы разместить эту часть на схеме, создавая part_instи 8 pin_instс.

Игнорируя поля данных, моя первая попытка создания схемы была

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
);

Основная проблема с этой схемой заключается в том, что a pin_instможет быть связано с part_instwith, part_id=1но pinимеет has part_id=2.

Я хотел бы избежать этой проблемы на уровне базы данных, а не на уровне приложений. Итак, я изменил свои первичные ключи, чтобы обеспечить это. Я пометил измененные строки с помощью --.

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              --
);

Мой метод заключается в том, что он загрязняет первичные ключи: везде, где я ссылаюсь на part_inst, мне нужно отслеживать как part_inst_idи, так и part_id. Есть ли другой способ, как я могу применить ограничение, pin_inst.part_inst.part_id = pin_inst.pin.part_idне будучи слишком многословным?

Снежный шар
источник
Вы также можете удалить pin_inst_idизбыточность. Вы можете использовать в (part_inst_id, part_id, pin_id)качестве первичного ключа.
ypercubeᵀᴹ
Две вещи: (а) разве 1OUT, 1IN-, 1IN +, GND, 2IN +, 2IN-, 2OUT и VCC не дают 11 выводов? (б) Я не понимаю твою первоначальную схему. Не может ли булавка использоваться более чем в одной части? Вам нужно отношение NN между штифтом и деталью, а не 1-N.
Маркус Юниус Брутус
@ user34332: (а) числа являются частью имен. Например, «2OUT» - это один контакт. Вот схематичный чертеж из чипа я говорю в этом вопросе. (б) я не согласен. Конечно, две части могут иметь контакт VCC (положительное напряжение питания, «общий коллектор»), но это логически разные контакты. Например, один вывод VCC обычно может потреблять 500 мкА, а другой - 250 мкА.
Снежок
@Snowball Это поможет другим понять вашу схему, если вы добавите SQL-скрипту с образцами данных.
ypercubeᵀᴹ
1
Тесно связанный вопрос.
Эрвин Брандштеттер

Ответы:

13

Минимальное решение

Одним из радикальных решений может быть pin_instполное удаление :

  part ←────────── pin
                   
part_inst ←───── pin_inst

В вашем вопросе нет ничего, что указывало бы на то, что вам действительно нужна избыточная таблица. Для pins, связанных с a part_inst, посмотрите на pins связанных part.

Это упростит код до:

create table part (    -- using singular terms for table names
    part_id bigserial primary key
);
create table pin (
    pin_id bigserial primary key,
    part_id bigint not null references part
);
create table part_inst (
    part_inst_id bigserial primary key,
    part_id bigint not null references part
);

Но ваш комментарий дал понять, что нам это не сойдет с рук ...

Альтернатива, если pin_instэто необходимо

Включение, part_idкак вы сделали, является самым простым решением с ограничениями внешнего ключа. Вы не можете ссылаться на таблицу «на расстоянии двух таблиц» с ограничениями внешнего ключа .

Но вы можете, по крайней мере, обойтись без «загрязнения» первичных ключей. Добавить UNIQUEограничения .

create table part (
    part_id bigserial primary key
);
create table pin (
    pin_id bigserial primary key,
    part_id bigint not null references part,
    unique(part_id, pin_id)         -- note sequence of columns
);
create table part_inst (
    part_inst_id bigserial primary key,
    part_id bigint not null references part,
    unique(part_id, part_inst_id)
);
create table pin_inst (
    pin_inst_id bigserial primary key,
    part_inst_id bigint not null,
    pin_id bigint not null,
    part_id bigint not,
    foreign key (part_id, pin_id) references pin,
    foreign key (part_id, part_inst_id) references part_inst
);

Я ставлю на part_idпервое место в уникальных ограничениях. Это не имеет отношения к ссылочной целостности, но имеет значение для производительности. Первичные ключи уже реализуют индексы для столбцов pk. Лучше иметь другой столбец первым в многоколоночных индексах, реализующих уникальные ограничения. Подробности по этим связанным вопросам:

Похожие вопросы по SO:

Альтернатива с триггерами

Вы можете прибегнуть к триггерным функциям, которые являются более гибкими, но более сложными, подверженными ошибкам и менее строгими. Выгода: Вы могли бы обойтись без part_inst.part_idи pin.part_id...

Эрвин Брандштеттер
источник
В нем есть несколько дополнительных столбцов pin_insts, но я опустил их для удобства чтения («Игнорирование полей данных, [...]»). Например, а pin_instможет быть помечен как вход или выход.
Снежок
@ Снежок: Было бы легко быть правдой. Я немного расширил ваше решение.
Эрвин Брандштеттер,
2
Ваше второе предложение хорошо подходит для моей ситуации. Я не знал, что внешний ключ может ссылаться на что-то кроме первичного ключа.
Снежок