Вопрос

Я подумываю о разработке схемы базы данных, подобной следующей:

Person (
  PersonID int primary key,
  PrimaryAddressID int not null,
  ...
)

Address (
  AddressID int primary key,
  PersonID int not null,
  ...
)

Person.PrimaryAddressID и адрес.PersonID будут внешними ключами к соответствующим таблицам.

Очевидная проблема заключается в том, что невозможно вставить что-либо ни в одну из таблиц.Есть ли какой-нибудь способ разработать рабочую схему, которая обеспечивает соблюдение требований к каждому Пользователю, имеющему основной адрес?

Это было полезно?

Решение

"Я считаю, что это невозможно.Вы не можете создать запись адреса, пока не узнаете идентификатор пользователя, и вы не сможете вставить запись person, пока не узнаете AddressID для поля PrimaryAddressId."

На первый взгляд, это утверждение кажется ТАКИМ привлекательным.Однако это довольно напыщенно.

Это очень распространенная проблема, с которой поставщики СУБД SQL пытаются бороться, возможно, уже несколько десятилетий.

Ключ в том, что вся проверка ограничений должна быть "отложена" до тех пор, пока не будут выполнены обе вставки.Это может быть достигнуто в различных формах.Транзакции базы данных могут предложить возможность сделать что-то вроде "УСТАНОВИТЬ проверку отложенных ограничений", и все готово (если бы не тот факт, что в этом конкретном примере вам, вероятно, пришлось бы очень сильно поработать с вашим дизайном, чтобы иметь возможность просто ОПРЕДЕЛИТЬ два ограничения FK, потому что одно из них просто НЕ ЯВЛЯЕТСЯ "истинным" FK в смысле SQL !).

Решения на основе триггеров, описанные здесь, достигают по существу того же эффекта, но они подвержены всем проблемам обслуживания, которые существуют при обеспечении целостности, обеспечиваемой приложением.

В своей работе Крис Дейт и Хью Дарвен описывают, что является имо истинным решением проблемы :множественное назначение.Это, по сути, возможность составить несколько различных инструкций обновления и заставить СУБД действовать с этим так, как если бы это была одна-единственная инструкция.Реализации этой концепции действительно существуют, но вы не найдете ни одной, в которой говорилось бы о SQL.

Другие советы

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

Это прекрасный пример отношений «многие ко многим». Для решения этой проблемы у вас должна быть промежуточная таблица PERSON_ADDRESS. Другими словами;

PERSON table
person_id (PK)

ADDRESS table
address_id (PK)

PERSON_ADDRESS
person_id (FK) <= PERSON
address_id (FK) <= ADDRESS
is_primary (BOOLEAN - Y/N)

Таким образом, вы можете назначить ЛИЧНОСТЬ нескольким адресам, а также повторно использовать АДРЕСНЫЕ записи в нескольких ЛИЦАХ (для членов семьи, сотрудников одной компании и т. д.). Используя поле is_primary в таблице PERSON_ADDRESS, вы можете определить, является ли эта комбинация person_addrees основным адресом для человека.

Второй FK (PersonId от Address to Person) слишком ограничен, ИМХО. Вы предлагаете, чтобы на одном адресе мог быть только один человек?

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

Я знаю, что я, вероятно, буду распят или что-то еще, но здесь идет ...

Я сделал это так для своего " особого очень уникального и нестандартного " деловая необходимость (= (Боже, я начинаю звучать как SQL DDL, даже когда говорю).

Вот пример:

CREATE TABLE IF NOT EXISTS PERSON(
    ID INT, 
    CONSTRAINT PRIMARY KEY (ID), 
    ADDRESS_ID INT NOT NULL DEFAULT 1, 
    DESCRIPTION VARCHAR(255), 
    CONSTRAINT PERSON_UQ UNIQUE KEY (ADDRESS_ID, ...));

INSERT INTO PERSON(ID, DESCRIPTION) 
    VALUES (1, 'GOVERNMENT');

CREATE TABLE IF NOT EXISTS ADDRESS(
    ID INT, 
    CONSTRAINT PRIMARY KEY (ID), 
    PERSON_ID INT NOT NULL DEFAULT 1, 
    DESCRIPTION VARCHAR(255), 
    CONSTRAINT ADDRESS_UQ UNIQUE KEY (PERSON_ID, ...), 
    CONSTRAINT ADDRESS_PERSON_FK FOREIGN KEY (PERSON_ID) REFERENCES PERSON(ID));

INSERT INTO ADDRESS(ID, DESCRIPTION) 
    VALUES (1, 'ABANDONED HOUSE AT THIS ADDRESS');

ALTER TABLE PERSON ADD CONSTRAINT PERSON_ADDRESS_FK FOREIGN KEY (ADDRESS_ID) REFERENCES ADDRESS(ID);

< ... жизнь продолжается ... независимо от того, предоставляете ли вы и обращаетесь или нет к человеку, и наоборот >

Я определил одну таблицу, затем другую таблицу, ссылающуюся на первую, а затем изменил первую, чтобы отразить ссылку на вторую (которая не существовала на момент создания первой таблицы). Он не предназначен для конкретной базы данных; если мне это нужно, я просто пробую это, и если это работает, то я использую это, если нет, то я стараюсь избежать этой потребности в дизайне (я не всегда могу это контролировать, иногда дизайн передается мне как есть) , если у вас есть адрес без человека, он принадлежит " правительство " человек. Если у вас есть & Бездомный & Quot; тогда он получает & заброшенный дом & адрес. Я запускаю процесс, чтобы определить, в каких домах нет пользователей

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top