如何创建一个结构连接行作为嵌套文档 PostgreSQL
Posted
技术标签:
【中文标题】如何创建一个结构连接行作为嵌套文档 PostgreSQL【英文标题】:How to create a structure joined rows as nested documents PostgreSQL 【发布时间】:2021-07-23 00:12:21 【问题描述】:我在 MongoDB 方面有经验,我正在学习 PostgreSQL。在这种情况下,我将 Node.js 与 pg
库一起使用。
我有 2 个表格:posts 和 cmets。我需要做的是进行一个查询,它将返回一个 posts 数组,并且每个返回的 post 都应该有一个嵌套的 cmets 数组。
现在,在 Mongo 中,我只需将帖子存储为 comments
字段,该字段将是对象的嵌套数组。在 PostgreSQL 中,我所做的是让每条评论都有一个 post_id
列,该列引用它的父帖子。我的问题是,如何检索结构类似于 mongo 示例的数组?也就是说,我如何返回一个帖子数组,每个帖子都有一个名为 comments
的字段,该字段将是相应评论行的数组?
现在我正在对 2 个表进行 JOIN,但最终发生的是我得到一个对象数组,其中包含帖子的所有字段以及 cmets 的所有字段。
现在发生了什么:
[
id: 1,
post_title: "something",
posted_at: "some date",
comment_text: "nice post",
author_id: 31
,
id: 1,
post_title: "something",
posted_at: "some date",
comment_text: "i dont like this post",
author_id: 4
]
我想要发生的事情
[
id: 1,
post_title: "something",
posted_at: "some date",
comments: [
comment_text: "nice post",
author_id: 31
,
comment_text: "i dont like this post",
author_id: 4
,
]
【问题讨论】:
SQL 集自然不支持嵌套结构。所以,问题变成了为什么需要数据库提供第二个结构?是什么阻止了您的应用程序层将记录集(根据第一个结构)转换为嵌套结构? 我不想这样做的原因是,至少在 mongo 中,在服务器(它是用 C++ 编写的)上做所有关于数据结构的事情要比在客户端(javascript)。我不知道相同的逻辑是否适用于 PostgreSQL,但如果不是,我绝对可以通过手动转换结构来解决我最初的问题。 【参考方案1】:我猜你的表结构在这里,但它应该足够接近。所以假设这是你的设置:
CREATE TABLE posts (id INTEGER, post_title TEXT, posted_at TEXT);
INSERT INTO posts
VALUES
(1, 'title 1', 'date 1'),
(2, 'title 2', 'date 2'),
(3, 'title 3', 'date 3');
CREATE TABLE comments (post_id INTEGER, comment_text TEXT, author_id INTEGER);
INSERT INTO comments
VALUES
(1, 'comment text 1', 34),
(3, 'comment text 3 a', 45),
(3, 'comment text 3 b', 67);
最简单的方法是这样的:
SELECT JSON_AGG(x ORDER BY id)
FROM (
SELECT p.id, p.post_title, p.posted_at, JSON_AGG(c) AS comments
FROM posts p
LEFT JOIN comments c
ON p.id = c.post_id
GROUP BY p.id, p.post_title, p.posted_at
) x;
返回近似你想要的结构:
[
"id": 1,
"post_title": "title 1",
"posted_at": "date 1",
"comments": [
"post_id": 1,
"comment_text": "comment text 1",
"author_id": 34
]
,
"id": 2,
"post_title": "title 2",
"posted_at": "date 2",
"comments": [null]
,
"id": 3,
"post_title": "title 3",
"posted_at": "date 3",
"comments": [
"post_id": 3,
"comment_text": "comment text 3 a",
"author_id": 45
,
"post_id": 3,
"comment_text": "comment text 3 b",
"author_id": 67
]
]
我注意到您的 cmets 数组不包含对父帖子 ID 的引用。仅选择字段子集的最佳方式取决于您的用例,但这应该是一种足够高效的方式:
SELECT JSON_AGG(x ORDER BY id)
FROM (
SELECT
p.id,
p.post_title,
p.posted_at,
(
SELECT JSON_AGG(c)
FROM (
SELECT c.comment_text, c.author_id
FROM comments c
WHERE c.post_id = p.id
) c
) AS comments
FROM posts p
) x;
返回
[
"id": 1,
"post_title": "title 1",
"posted_at": "date 1",
"comments": [
"comment_text": "comment text 1",
"author_id": 34
]
,
"id": 2,
"post_title": "title 2",
"posted_at": "date 2",
"comments": null
,
"id": 3,
"post_title": "title 3",
"posted_at": "date 3",
"comments": [
"comment_text": "comment text 3 a",
"author_id": 45
,
"comment_text": "comment text 3 b",
"author_id": 67
]
]
显然,post 2 的 cmets 在它们之间有所不同,您必须使用一些逻辑来决定您希望 no cmets 的样子。
【讨论】:
非常感谢!我忘了添加它(抱歉),但 cmets 表确实包含一个引用父帖子的post_id
col。
应该注意的是,这样做会将结果从二进制表示形式转换为字符串。这增加了数据库的工作量,增加了网络流量,并且需要在本地解析字符串以创建可用的数据结构。这并不是说这是错误的做法,只是明确指出涉及许多间接费用;它不是免费的。以上是关于如何创建一个结构连接行作为嵌套文档 PostgreSQL的主要内容,如果未能解决你的问题,请参考以下文章
Elasticsearch & X-Pack:如何从嵌套文档中获取顶点/连接