pik*_*ikk 4 postgresql inheritance triggers database-design referential-integrity
我是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),
);
Run Code Online (Sandbox Code Playgroud)
根据这些表,当我想将数据INSERT到Adress表中时,Postgres会给出错误:
错误:在表"地址"上插入或更新违反外键约束"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"
Run Code Online (Sandbox Code Playgroud)
学生表
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"
Run Code Online (Sandbox Code Playgroud)
员工表
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
Run Code Online (Sandbox Code Playgroud)
首先用以下方法摆脱FK:
alter table address drop constraint address_person_id_fkey
Run Code Online (Sandbox Code Playgroud)
如果抱怨不存在作为一个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;
Run Code Online (Sandbox Code Playgroud)
并像这样附加它:
create trigger pseudo_fk_for_address_trigger before insert or update on address
for each row execute procedure pseudo_fk_for_address();
Run Code Online (Sandbox Code Playgroud)
如果您尝试为不存在的人person(包括从其继承的表)添加地址,则会收到类似的错误消息:
playpen=> insert into address (person_id, email, country, citycode, city, addressline) values (3, 'ab', 'b', 2, 'c', 'd');
ERROR: No such person: 3
Run Code Online (Sandbox Code Playgroud)
您希望添加一个BEFORE DELETE触发器以person避免悬挂引用,该基本结构几乎相同。您可能希望建立索引address.person_id以帮助支持BEFORE DELETE触发器。
参考文献:
外键不是继承的.如果外键指向表person,则该表中必须包含相同的值.PostgreSQL中继承的实现是有限的,我引用手册中的 "注意事项"一章:
这种情况没有好的解决方法.
这包括 @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)
Run Code Online (Sandbox Code Playgroud)
这也消除id了继承树中列中两个可能重复的来源之一.(另一个是你仍然可以输入student存在于employee或中的ID person.继承系统中的另一个警告.所以,永远不要手动插入或更改id.将它保留到列默认和序列.
3)"自然"模型将在address和之间建立n:m关系person.对于你的模型,我将使用一个额外的表来实现它,person_address其中address_id引用表address并且person_id只有外键约束的梦想(原始问题).
你拥有它的方式,一个地址永远不会被一个人居住.也许这对你的目的来说已经足够了.这样你就可以将整个地址嵌入到人员表中(并让学生和员工继承它)以完全避免你的外键问题.
| 归档时间: |
|
| 查看次数: |
2006 次 |
| 最近记录: |