在继承表上使用触发器来替换外键
我是 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) 是 不存在于表“person”中。
我在 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
I'm new to PostgreSQL. I have tables like:
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),
);
According to these tables, when I want to INSERT data into Adress
table, Postgres gives that error:
ERROR: insert or update on table "address" violates foreign key
constraint "address_person_id_fkey" DETAIL: Key (person_id)=(1) is
not present in table "person".
I've learned that in Postgres
indexes (including unique constraints) and foreign key constraints
only apply to single tables, not to their inheritance children.
My question is how can I fix this with using triggers? Sample code would be very useful.
After inserting a few rows to child tables, I can see the data with 'SELECT * FROM Person;' as well. It looks like:
Person Table
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"
Student Table
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"
Employee Table
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
首先用这样的东西摆脱 FK:
如果抱怨没有
address_person_id_fkey
约束,那么在psql
中使用\d address;
找出 FK 的名称。然后像这样的简单触发器应该可以解决问题:
并像这样附加它:
然后,如果您尝试添加
person
中不存在的某人的地址,您将收到这样的错误(包括继承自它的表):您希望向
person
添加一个 BEFORE DELETE 触发器以避免悬空引用,该基本结构几乎是相同的。您可能还需要address.person_id
上的索引来帮助支持 BEFORE DELETE 触发器。参考文献:
提高
First get rid of the FK with something like this:
If that complains about there not being an
address_person_id_fkey
constraint then use\d address;
inpsql
to find out what the FK is called.Then a simple trigger like this should do the trick:
And attach it like this:
Then you'll get an error like this if you try to add an address for someone that doesn't exist in
person
(including the tables that inherit from it):You'd want to add a BEFORE DELETE trigger to
person
to avoid dangling references, that basic structure would be pretty much the same. You might want an index onaddress.person_id
to help support the BEFORE DELETE trigger as well.References:
RAISE
外键不被继承。如果外键指向表
person
,那么该表中必须有相同的值。继承的实现在 PostgreSQL 中是有限的,我引用了“Caveats”一章 手册中:这包括@Mu 提议的触发器。为了保证引用完整性,您需要的不仅仅是触发器
ON INSERT
。我不会尝试那样做。如果删除一个人会发生什么?改ID了吗?我认为根本不使用继承。如果您仍然想要或必须这样做,我会建议对您的数据模型进行一些更改。
1)
电子邮件
不应该出现在地址表中,它与地址以及与此人的所有内容无关。将其移至表person
。错位的原因可能是您想要强制唯一性。根本不使用继承的另一个原因。2) 列
id_student
和id_employee
是多余的。请使用继承的列id
作为主键。只需向您的子表添加约束即可:这还消除了继承树上
id
列中可能重复的两个来源之一。 (另一个是,您仍然可以将 ID 输入到student
中,这些 ID 存在于employee
或person
中。继承系统中的另一个警告。所以,切勿手动插入或更改id
。将其保留为列默认值和顺序。3) “自然”模型将在 < 之间建立 n:m 关系。代码>地址 和
人
。对于您的模型,我将使用附加表person_address
来实现它,其中 address_id 引用表address
和person_id
仅梦想外键约束(原始问题)。按照您的方式,一个地址永远不可能有多个人居住。也许这足以满足您的目的。这样,您也可以将整个地址嵌入到人员表中(并让学生和员工继承它),以完全避免外键问题。
Foreign keys are not inherited. If the foreign key points to table
person
, then the same value has to be in that table. The implementation of inheritance is limited in PostgreSQL, I quote from the chapter "Caveats" in the manual:This includes @Mu's proposed trigger. You would need a lot more than a trigger
ON INSERT
to guarantee referential integrity. I would not try that. What happens if you delete a person? Change it's ID?I would consider not to use inheritance at all. If you still want or have to, I would propose several changes to your data model.
1)
email
should not be in the address table, it has nothing to to with an address and everything with the person. Move it to the tableperson
. The reason for the misplacement may be that you want to enforce uniqueness. Another reason not to use inheritance at all.2) The columns
id_student
andid_employee
are redundant. Use the inherited columnid
as primary keys instead. Just add a constraint to your child tables:This also eliminates one of two sources of possible duplicates in the
id
column over the inheritance tree. (The other one is that you can still enter IDs intostudent
that are present inemployee
orperson
. Another caveat in the inheritance system. So, never manually insert or change theid
. Leave it to the column default and the sequence.3) The "natural" model would be to have an n:m relationship between
address
andperson
. For your model I would implement that with an additional tableperson_address
where address_id references tableaddress
andperson_id
only dreams of a foreign key constraint (original problem).The way you have it, an address can never be inhabited by more than one person. Maybe it's good enough for your purpose. This way you might as well imbed the whole address into the person table (and have student and employee inherit it) to avoid your foreign key problem altogether.