4

我是 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”上插入或更新违反了外键约束“address_person_id_fkey” 详细信息:表“person”中不存在键 (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
4

2 回答 2

5

外键不被继承。如果外键指向 table person,则该表中必须有相同的值。继承的实现在 PostgreSQL 中是有限的,我引用了手册中的“注意事项”一章:

这种情况没有好的解决方法。

包括@Mu 提议的触发器。您需要的不仅仅是触发器ON INSERT来保证引用完整性。我不会那样做的。如果删除一个人会发生什么?换身份证?

我会考虑根本不使用继承。如果您仍然想要或必须这样做,我会建议对您的数据模型进行一些更改。

  • 1)email不应该在地址表中,它与地址无关,与人无关。把它移到桌子上person。错位的原因可能是您想强制执行唯一性。根本不使用继承的另一个原因。

  • 2)列id_studentid_employee是多余的。改为使用继承的列id作为主键。只需向您的子表添加一个约束:

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

    id这也消除了继承树上列中可能重复的两个来源之一。(另一个是您仍然可以输入student存在于employeeor中的 ID person。继承系统中的另一个警告。因此,切勿手动插入或更改id. 将其保留为列默认值和序列。

  • address3) “自然”模型将在和之间具有 n:m 关系person。对于您的模型,我将使用一个附加表来实现它,person_address其中 address_id 引用表address并且person_id只梦想外键约束(原始问题)。

    按照你的方式,一个地址永远不会被超过一个人居住。也许它足以满足您的目的。这样,您不妨将整个地址嵌入到人员表中(并让学生和员工继承它),以完全避免外键问题

于 2011-12-12T04:40:38.070 回答
5

首先用这样的东西摆脱FK:

alter table address drop constraint address_person_id_fkey

如果这抱怨没有address_person_id_fkey约束,那么使用\d address;inpsql找出 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 触发器。

参考:

于 2011-12-12T03:08:58.677 回答