Используйте триггеры на унаследованных таблицах для замены внешних ключей - PullRequest
3 голосов
/ 12 декабря 2011

Я новичок в PostgreSQL. У меня есть таблицы как:

CREATE TABLE Person (
  ID SERIAL PRIMARY KEY,
  Name VARCHAR(32) NOT NULL DEFAULT '',
  Surname VARCHAR(32) NOT NULL DEFAULT '',
  Birthday DATE,
  Gender VARCHAR(8)
);

-- Student table inherits from person
CREATE TABLE Student (
  ID_Student SERIAL PRIMARY KEY,
  MajorDept VARCHAR(32),
) INHERITS(Person);

-- Student table inherits from person
CREATE TABLE Employee (
  ID_Employee SERIAL PRIMARY KEY,
  Position VARCHAR(32),
  Rank VARCHAR(32),
  Salary NUMERIC(12,2)
) INHERITS(Person);

-- Address table references person
CREATE TABLE Address (
  ID_Address SERIAL PRIMARY KEY,
  Person_id INTEGER REFERENCES Person(ID) NOT NULL,
  Email VARCHAR(32) UNIQUE,
  Country VARCHAR(32),
  CityCode INTEGER,
  City VARCHAR(32),
  AddressLine VARCHAR(60),
);

Согласно этим таблицам, когда я хочу вставить данные в таблицу Adress, Postgres выдает эту ошибку:

ОШИБКА: вставка или обновление таблицы «адрес» нарушает внешний ключ ограничение "address_person_id_fkey" ДЕТАЛИ: Ключ (person_id) = (1) отсутствует в таблице "персона".

Я узнал это в Postgres

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

Мой вопрос: как я могу это исправить с помощью триггеров? Пример кода был бы очень полезен.

После вставки нескольких строк в дочерние таблицы я могу увидеть данные с помощью команды «SELECT * FROM Person;» также. Похоже:

Персональный стол

1;"Bill";"Smith";"1985-05-10";"male"
2;"Jenny";"Brown";"1986-08-12";"female"
3;"Bob";"Morgan";"1986-06-11";"male"
4;"Katniss";"Everdeen";"1970-08-12";"female"
5;"Peter";"Everdeen";"1968-08-12";"male"

Стол ученический

1;"Bill";"Smith";"1985-05-10";"male";1;"chemistry"
2;"Jenny";"Brown";"1986-08-12";"female";2;"physics"
3;"Bob";"Morgan";"1986-06-11";"male";3;"physics"

Таблица сотрудников

4;"Katniss";"Everdeen";"1970-08-12";"female";1;"Prof";"1";3500.00
5;"Peter";"Everdeen";"1968-08-12";"male";2;"Assist-Prof";"5";1800.00

Ответы [ 2 ]

5 голосов
/ 12 декабря 2011

Внешние ключи не наследуются.Если внешний ключ указывает на таблицу person, то в этой таблице должно быть то же значение.Реализация наследования ограничена в PostgreSQL, я цитирую из главы «Предостережения» в руководстве :

Нет хорошего обходного пути для этого случая.

Это включает @ предложенный Му триггер.Вам потребуется намного больше, чем триггер ON INSERT, чтобы гарантировать ссылочную целостность.Я бы не стал это пробовать.Что произойдет, если вы удалите человека?Изменить ID?

Я бы посчитал , а не , чтобы использовать наследование вообще.Если вы все еще хотите или должны, я бы предложил несколько изменений в вашей модели данных.

  • 1) email не должно быть в таблице адресов, этоне имеет ничего общего с адресом и все с человеком.Переместите его к столу person.Причиной смещения может быть то, что вы хотите обеспечить уникальность.Другая причина вообще не использовать наследование.

  • 2) Столбцы id_student и id_employee являются избыточными.Вместо этого используйте унаследованный столбец id в качестве первичных ключей.Просто добавьте ограничение к вашим дочерним таблицам:

    CONSTRAINT student_pkey PRIMARY KEY (id)
    CONSTRAINT employee_pkey PRIMARY KEY (id)
    

    Это также устраняет один из двух источников возможных дубликатов в столбце id в дереве наследования.(Другой способ заключается в том, что вы все равно можете вводить идентификаторы в student, которые присутствуют в employee или person. Еще одна оговорка в системе наследования. Поэтому никогда не вставляйте и не изменяйте id вручную. Оставьте его настолбец по умолчанию и последовательность.

  • 3) "Естественная" модель должна иметь отношение n: m между address и person.Для вашей модели я бы реализовал это с помощью дополнительной таблицы person_address, где address_id ссылается на таблицу address и person_id, которая только мечтает об ограничении внешнего ключа (исходная проблема).

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

4 голосов
/ 12 декабря 2011

Сначала избавьтесь от FK с помощью чего-то вроде этого:

alter table address drop constraint address_person_id_fkey

Если это жалуется на отсутствие ограничения address_person_id_fkey, тогда используйте \d address; в psql, чтобы узнать, что такое FKназывается.

Тогда простой триггер, подобный этому, должен добиться цели:

create or replace function pseudo_fk_for_address() returns trigger as $$
begin
    if not exists(select 1 from person where id = new.person_id) then
        raise exception 'No such person: %', new.person_id;
    end if;
    return new;
end;
$$ language plpgsql;

И прикрепить его так:

create trigger pseudo_fk_for_address_trigger before insert or update on address 
for each row execute procedure pseudo_fk_for_address();

Тогда вы получитеошибка, подобная этой, если вы пытаетесь добавить адрес для кого-то, чего нет в person (включая таблицы, наследующие его):

playpen=> insert into address (person_id, email, country, citycode, city, addressline) values (3, 'ab', 'b', 2, 'c', 'd');
ERROR:  No such person: 3

Вы хотите добавить триггер BEFORE DELETEна person, чтобы избежать висячих ссылок, эта базовая структура была бы почти такой же.Возможно, вы захотите, чтобы индекс на address.person_id также поддерживал триггер BEFORE DELETE.

Ссылки:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...