Проектирование реляционной БД

Рейтинг: 0Ответов: 1Опубликовано: 15.01.2023

Я разрабатываю базу данных. У меня встал вопрос по следующей части проектирования. В бд есть следующие 3 таблицы: Таблицы

Логика такая: в одно подразделение может входить много клиентов. В тоже время это подразделение может выполнять несколько рассылок своим клиентам. Один клиент может получить несколько рассылок от своего подразделения. Как видно из скрина получается некая циклическая связь между таблицами. Вопрос: такое вообще возможно, просто я слышал, что такая связь - это ошибка. Правильно ли я поступаю? Если нет, то как сделать лучше?

Ответы

▲ 1

В вашей схеме нет зацикленности. Ограничения внешних ключей определяют порядок внесения данных и как видно из схемы это вполне возможно. Если вы посмотрите на реальное зацикливание внешних ключей то убедитесь что попытка внесения данных в любую из участвующих в петле связей таблиц попросту не возможна. Давайте посмотрим не примере трех таблиц ссылающихся друг на друга (для близости к вашему варианту):

CREATE TABLE a (
    id serial NOT NULL,
    id_b int NOT NULL,
    CONSTRAINT a_pkey PRIMARY KEY (id)
);

CREATE TABLE b (
    id serial NOT NULL,
    id_c int NOT NULL,
    CONSTRAINT b_pkey PRIMARY KEY (id)
);

CREATE TABLE c (
    id serial NOT NULL,
    id_a int NOT NULL,
    CONSTRAINT c_pkey PRIMARY KEY (id)
); 

Определим ограничения внешних ключей:

ALTER TABLE IF EXISTS a
    ADD CONSTRAINT lnk_a_b FOREIGN KEY (id_b)
    REFERENCES b (id) MATCH FULL
    ON UPDATE CASCADE
    ON DELETE CASCADE;

ALTER TABLE IF EXISTS b
    ADD CONSTRAINT lnk_b_c FOREIGN KEY (id_c)
    REFERENCES c (id) MATCH FULL
    ON UPDATE CASCADE
    ON DELETE CASCADE;

ALTER TABLE IF EXISTS c
    ADD CONSTRAINT lnk_c_a FOREIGN KEY (id_a)
    REFERENCES a (id) MATCH FULL
    ON UPDATE CASCADE
    ON DELETE CASCADE;

Попробуем добавить данные в каждую таблицу:

INSERT INTO a(id_b) VALUES(1);

ERROR: insert or update on table "a" violates foreign key constraint "lnk_a_b" DETAIL: Key (id_b)=(1) is not present in table "b".

INSERT INTO b(id_c) VALUES(1);

ERROR: insert or update on table "b" violates foreign key constraint "lnk_b_c" DETAIL: Key (id_c)=(1) is not present in table "c".

INSERT INTO c(id_a) VALUES(1);

ERROR: insert or update on table "c" violates foreign key constraint "lnk_c_a" DETAIL: Key (id_a)=(1) is not present in table "a".

Можете попробовать сами здесь

В вашей схеме код клиента включен в качестве поля в таблицу рассылок. Это значит, что в одной уникальной рассылке может быть только один клиент. Если подумать то рассылка должна включать множество клиентов и один клиент может входить в несколько рассылок. Это отношение "многие ко многим" реализуется через промежуточную таблицу с составным ключом ид_рассылки + ид_клиента и связи должны быть назначены не напрямую а через эту промежуточную таблицу, ид_клиента убрать из таблицы рассылок. Тогда у вас получится то что вам нужно.