我是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”DETAIL:键(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
答案 0 :(得分:5)
外键不是继承的。如果外键指向表person
,则该表中必须包含相同的值。 PostgreSQL中继承的实现是有限的,我引用了章节" Caveats" in the manual:
这种情况没有好的解决方法。
包含 @Mu的提议触发器。您需要的不仅仅是触发器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。继承系统中的另一个警告。所以,永远不要手动插入或更改{{ 1}}。将其保留为默认列和序列。
3)" natural"模型将是 id
和address
之间的n:m关系。对于您的模型,我将使用额外的表person
来实现它,其中address_id引用表person_address
和address
仅梦想外键约束(原始问题)。
你拥有它的方式,一个地址永远不会被一个人居住。也许这对你的目的来说足够好了。这样你就可以将整个地址嵌入到人员表中(让学生和员工继承它),以完全避免你的外键问题。
答案 1 :(得分:4)
首先用这样的东西摆脱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
你想要向person
添加一个BEFORE DELETE触发器,以避免悬空引用,基本结构几乎相同。您可能希望address.person_id
上的索引也可以帮助支持BEFORE DELETE触发器。
参考文献: