也许我的问题比我最初想象的要简单得多。我的问题实际上是如何查询关系数据库中的复杂对象(带有嵌套对象)。也许我只需要分别查询每个容器级别,并在 go 代码中进行一些处理,以编程方式重新组装结构树......我最初的想法是直接在大型查询中完成这项工作,以提高性能和简单性,但也许关系数据库不是我想在这里完成的事情的最佳工具。或者也许使用 json 甚至 ORM 抽象是这种情况的唯一替代方案。我应该看看流行的 go ORM 代码,看看他们如何查询这样的表结构,看看他们是如何做到的。
我正在开发一个个人项目,该项目意味着在每个节点级别(允许继承)上通过权限管理来表示分层数据,为简单起见,我将其称为容器。就像标准文件系统中的文件夹和文件的结构。我正在开发一个堆栈,一方面是 PostgreSQL,另一方面是 go(lang) 作为后端语言。我是 PostgreSQL 的初学者,所以如果我的设计完全错误,请毫不犹豫地告诉我......
我的问题来自于在 PGSQL 中查询这种结构,因为每个容器都有公共列,但也有特定列。因此,存在继承和递归的概念,但也存在多态性,因为每个级别都是具有公共列的容器。需要注意的一点是,由于我为每个容器使用单独的表,因此我的结构是静态的,并且不会有无限的子级别。
这是一个简化的图:
此图中的每个容器都有一个专用的连接表,其中包含一个权限表,用于按容器级别处理权限。但我认为这与此无关。
在我正在构建的项目中,我试图通过将树表示为包含子对象的对象来一次性查询此结构,就像 json 所做的那样。我目前通过在一个查询中使用 json_object 和 json_agg 函数的组合成功查询并获得我想要的结果,但由于我使用 golang,我想使用标准 SQL 或 PL/SQL 查询以本机类型返回结果并避免json 编组。这个想法是为每个容器类型创建一个具有相应字段的结构,并为每个容器级别(此处为 A 和 B)创建一个包含子容器的数组或切片。
由于这个问题,我开始质疑我的结构,我想象只有一个表容器,其中包含一个 JSON 列,用于其中的所有特定数据值,但这意味着在 golang 中进行处理以在每个相应的父结构中分派子结构因为查询的结果将处于同一级别...
我认为这种情况很常见,但我没有找到任何资源......所以我想知道其他人如何让他们的编程对象充满这种结构? 也许 PostgreSQL 不是正确的工具,但对于我项目的其余部分,我需要有数据的关系结构,并且我会避免混合多种技术以避免复杂性......
因此,如果您有任何想法……或者对该模型的批评者,请随时发表评论。非常感谢。
您可以尝试将公共列提取到单独的表中,然后可以使用递归查询来获取分层位。
类似:
CREATE TABLE dt_containiers (
id serial,
parent_id integer,
name taxt,
...
CONSTRAINT dt_containiers_pk PRIMARY KEY ( id ),
CONSTRAINT dt_containiers_fk01 FOREIGN KEY ( parent_id ) REFERENCES dt_containiers ( id )
) ;
然后您可以使用类似以下内容进行查询:
WITH RECURSIVE toc AS (
SELECT base.id,
'{}'::integer[] AS parents,
base.parent_id,
0 AS tree_depth,
ARRAY [ dense_rank () OVER (
ORDER BY base.name ) ] AS outln,
ARRAY[base.id] AS id_path,
ARRAY[base.name] AS name_path
FROM dt_containiers base
WHERE base.parent_id IS NULL
UNION ALL
SELECT base.id,
q.parents || base.parent_id,
base.parent_id,
( q.tree_depth + 1 ) AS tree_depth,
( q.outln || dense_rank () OVER (
PARTITION BY base.parent_id
ORDER BY base.name ) ) AS outln,
q.id_path || base.id,
q.name_path || base.name
FROM dt_containiers base
JOIN toc q
ON ( base.parent_id = q.id
AND NOT base.id = ANY ( q.parents ) ) -- avoid cyclic references
)
SELECT toc.id,
toc.parent_id,
--toc.parents,
toc.id_path[1] AS top_parent_id,
toc.id_path,
toc.tree_depth,
pg_catalog.array_to_string ( toc.outln, '.'::text ) AS outln,
pg_catalog.array_to_string ( toc.name_path, ': '::text ) AS name_path
FROM toc
...
;