postgresql-使用混合节点类型设计树层次结构(继承没有帮助!)
我有一个关于在postgresql(9.1)中实现继承的问题 其目的是建立一个地理层次模型,在该模型中,国家、州和大陆可以混合起来创建“区域”。然后这些 地区也可以和国家等混为一谈,形成真正令人敬畏的地区等级体系 所以在我的逻辑模型中,一切都是一种“场所”。区域树可以通过使用两个“位置”指定边线来构造。设计如下,易于在Java层管理postgresql-使用混合节点类型设计树层次结构(继承没有帮助!),postgresql,tree,Postgresql,Tree,我有一个关于在postgresql(9.1)中实现继承的问题 其目的是建立一个地理层次模型,在该模型中,国家、州和大陆可以混合起来创建“区域”。然后这些 地区也可以和国家等混为一谈,形成真正令人敬畏的地区等级体系 所以在我的逻辑模型中,一切都是一种“场所”。区域树可以通过使用两个“位置”指定边线来构造。设计如下,易于在Java层管理 create table place_t ( place_id serial primary key, pl
create table place_t (
place_id serial primary key,
place_type varchar(10)
);
create table country_t (
short_name varchar(30) unique,
name varchar(255) null
) inherits(place_t);
create table region_t(
short_name varchar(30),
hierarchy_id integer, -- references hierarchy_t(hierarchy_id)
unique(short_name) -- (short_name,hierarchy_id)
) inherits(place_t);
create table region_hier_t(
parent integer references place_t(place_id), -- would prefer FK region_t(place_id)
child integer references place_t(place_id),
primary key(parent,child)
);
insert into region_t values(DEFAULT, 'region', 'NA', 'north american ops');
insert into region_t values(DEFAULT, 'region', 'EMEA', 'europe and middle east');
insert into country_t values(DEFAULT, 'country', 'US', 'USD', 'united states');
insert into country_t values(DEFAULT, 'country', 'CN', 'CND', 'canada');
到目前为止还不错。但以下几点失败了:
insert into region_hier_t
select p.place_id, c.place_id
from region_t as p, country_t as c
where p.short_name = 'NA' and c.short_name = 'US';
原因是前4次插入没有在“place_t”中创建任何行。RTFM!博士后的医生实际上提到了这一点
问题是——有解决办法吗?通过在地区和国家上插入触发器来实现我自己的“继承”是我唯一能想到的
第二个问题是——这种混合节点树结构有更好的设计吗
出于某些原因,我不想太依赖postgres的contrib功能。也许这很愚蠢,请随意插话,但要温柔(并且只有在回答了另一个问题之后)
谢谢,region\u hier\t表中父列和子列上的引用是错误的,因为如果您的引用调用另一个表
(子整数引用place\t(place\u id)),则无法插入来自country\t的键。
;您可以删除它们或添加新的。
因此,让我们选择第二个选项,并为引用表region\u t和country\t添加一个与给定键匹配的唯一约束:
ALTER TABLE region_t
ADD CONSTRAINT pk_region_t PRIMARY KEY(place_id );
ALTER TABLE country_t
ADD CONSTRAINT pk_country_t PRIMARY KEY(place_id );
区域\u hier\t的正确CREATE语句为:
create table region_hier_t(
parent integer references region_t(place_id),
child integer references country_t(place_id),
primary key(parent,child)
);
最后,您可以运行INSERT
因此,正如您所看到的,您需要做很多改进。也许你应该重新考虑你的设计。看看这个答案:它比您的解决方案简单得多,而且更易于维护
但如果您想继续使用您的解决方案,请不要忘记在子表上设置主键(如上所示)。仅检查约束和非空约束由其子项继承,而您尚未执行此操作
我发现您的其他插件无法正常工作:
insert into region_t values(DEFAULT, 'region', 'NA', 'north american ops');
ERROR: invalid input syntax for integer: "north american ops"
LINE 1: ...ert into region_t values(DEFAULT, 'region', 'NA', 'north ame...
因此,列分配也存在问题。因此,PostgreSQL中的继承与典型OOP语言中使用的继承有些不同。特别是,“超类”表不会自动填充。如果我必须使用我自己的触发器来实现这一点,那么我就没有留给继承结构的用例了 所以我放弃了Postgresql继承,创建了自己的“place\t”表。以及“国家”、“州”、“县”和“地区”子表,通过“地点id”链接到父“地点” 在这些子表上,我创建了一个before insert/update行级触发器,以确保“place\u id”引用“place\u t”中的有效行,并且以后不会更改引用。注意,子表中的“place_id”的行为应该类似于一次写入多次读取 现在,我可以插入世界地理信息。此外,定义一个新的“区域”。我创建了一个“region\u composition\t”来记录区域层次结构的边缘,父级是对“region\t”的引用,子级是对“place\t”的引用
到目前为止还不错。现在的挑战是如何抑制任何更新/删除层叠效果。解决方法是去掉外键来放置,而是执行以下操作:
CREATE FUNCTION place_t_exists(id int)
RETURNS bool LANGUAGE SQL AS
$$
SELECT count(*) = 1 FROM place_t;
$$;
CREATE FUNCTION fkey_place_t() RETURNS TRIGGER
LANGUAGE PLPGSQL AS $$
BEGIN;
IF place_t_exists(TG_ARGV[1]) THEN RETURN NEW
ELSE RAISE EXCEPTION 'place_t does not exist';
END IF;
END;
$$;
当层次结构节点存在时,子表上还需要一些约束:
CREATE FUNCTION hierarchy_exists(id int) RETURNS BOOL LANGUAGE SQL AS
$$
SELECT COUNT(*) > 0 FROM region_heir_t WHERE parent = $1 or child = $1;
$$;
CREATE OR REPLACE FUNCTION fkey_hierarchy_trigger() RETURNS trigger LANGUAGE PLPGSQL AS
$$
BEGIN
IF hierarchy_exists(old.place_id) THEN RAISE EXCEPTION 'Hierarchy node still exists';
ELSE RETURN OLD;
END;
$$;
然后您可以创建触发器:
CREATE CONSTRAINT TRIGGER fkey_place_parent AFTER INSERT OR UPDATE TO region_hier_t
FOR EACH ROW EXECUTE PROCEDURE fkey_place_t(new.parent);
CREATE CONSTRAINT TRIGGER fkey_place_child AFTER INSERT OR UPDATE TO region_hier_t
FOR EACH ROW EXECUTE PROCEDURE fkey_place_t(new.child);
然后对于每个place\t子表:
CREATE CONSTRAINT TRIGGER fkey_hier_t TO [child_table]
FOR EACH ROW EXECUTE PROCEDURE fkey_hierarchy_trigger();
这个解决方案可能不值得,但如果需要的话,知道如何去做是值得的。关于异构层次结构的类似EAV的模型,请参见我的答案:@wildplasser感谢您的链接,非常有用。(+1)我认为postgresql中的继承离快乐大餐还有几步之遥,因为它实际上没有生成可引用的超类集合对象。你已经一针见血了。谢谢你的地址链接。但我有一个混合袋的客户定义和地缘政治等级的世界。