【问题标题】:How to implement a hierarchical structure of several nested composite types in PostgreSQL?如何在PostgreSQL中实现几种嵌套复合类型的层次结构?
【发布时间】:2020-10-23 20:09:56
【问题描述】:

我正在尝试在 PostgreSQL 11.7 中实现一个数据库,它应该代表几个嵌套复合类型的层次结构。目前我有以下定义(简化):

CREATE TYPE type_school as (code integer, descr text); 
CREATE TYPE type_district as (code integer, descr text, schools type_school[]);
CREATE TYPE type_city as (code integer, descr text, districts type_district[]);
CREATE TYPE type_country as (code integer, descr text, cities type_city[]);

还有一张桌子:

CREATE TABLE countries (country type_country);

例如,这些应该是有效记录(descr 列是可选的,不感兴趣):

代码为 1 的国家/地区,城市 3,4,5,地区为 {1,2}、{1,3}、{3,6}
国家代码 2,城市 3,6,地区 {3,7}, {7,9}

为了填充表格,我对国家/地区使用 INSERT,对其他元素使用 UPDATE:

INSERT INTO countries values(ROW(1, 'country descr', ARRAY[]::type_city[]));
UPDATE countries SET(country.city[1].code, country.city[1].descr) = (1, 'city descr') WHERE (country).code = 1;
UPDATE countries SET(country.city[1].district[2].code, country.city[1].district[2].descr) = (2, 'district descr') WHERE (country).code = 1;

此设置工作正常,因为我可以执行大部分必要的查询。但是,我认为这不是正确的方法。我是一名 C 程序员,没有数据库编程经验。我将这种排列视为struct 元素的数组,由更多struct 数组组成。而且我习惯于通过索引来访问元素,这就是你在这个实现中看到的。我想拥有数据库的一些特性,比如约束。然而,这些在 PostgreSQL 类型上是不可能的,只能在表上。如果我将数组定义为表,我不知道如何编写INSERT 查询来访问内部表。根据一些网站,在 PostgreSQL 中嵌套表是不可能的,他们建议使用数组。是否可以在复合类型数组中强制执行约束?我在网上找到的另一个建议是使用 ltree 扩展名。但是在我看来,树元素都是相同的类型,并且每个级别都有不同的类型。同样在我当前的实现中,我不知道如何删除某个元素及其所有子元素。所以我的问题是:

应该如何实现一个表来表示一个树状结构,由 4 个级别组成,每个级别代表一种不同的类型,以便可以为每种类型的元素指定约束?甚至可以用关系数据库来做到这一点吗?为了清楚起见,我要区分元素的只是一个索引,每个元素仅通过其路径 country[i]->city[j]->district[k]->school[l] 唯一标识。

【问题讨论】:

  • 不,这都是错误的。规范化数据库模型:每列应该只包含一个(“原子”)值。将数据放在具有外键关系的不同表中。
  • 这是否意味着底部表(学校)应该有 3 个外键 - 国家、城市和地区,而一个地区将有 2 个国家和城市?
  • school 将有一个到 district 的外键,district 将有一个到 city 的外键,city 将有一个到 country 的外键。要选择数据,请加入表格;这是非常有效的。
  • 好的,但是由于我可以在不同的国家和城市有重复的城市或地区代码,我如何创建外键?如果城市的主键是 (country_code, city_code) 我可以将国家 1 与城市 1,2 和国家 2 与城市 1,2。但是如何将此组合引用为区域中的外键?
  • 那么外键必须同时引用它们。人们通常使用人工生成的数字主键来避免这种情况,但主键由两列组成是没有问题的。

标签: database postgresql


【解决方案1】:

感谢Laurenz Albe 为我指明了正确的方向。我正在发布一个带有代码的解决方案,以防其他人需要一个工作示例。

首先创建顶层表:

CREATE TABLE countries(code INTEGER, descr TEXT, PRIMARY KEY(code));

下一级表使用其代码和顶级表中的代码作为其主键。这样,“code”列的重复值是可能的,因为 code_country 和 code_city 的组合必须是唯一的:

CREATE TABLE cities(code INTEGER, 
descr TEXT, 
code_country INTEGER,
FOREIGN KEY(code_country) REFERENCES countries(code),
PRIMARY KEY(code_country, code));

第三层的表应该使用其父级的主键作为外键。这是通过省略 REFERENCES 指令中的列名来实现的:

CREATE TABLE district(code INTEGER, 
descr TEXT,
code_country INTEGER,
code_city INTEGER,
FOREIGN KEY(code_country, code_city) REFERENCES cities,
PRIMARY KEY(code_country, code_city, code));

添加约束现在很简单。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-19
    • 2023-03-26
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多