假设我使用以下 SQL 创建了两个表,
这样 post
有很多 comment
:
CREATE TABLE IF NOT EXISTS post (
id SERIAL PRIMARY KEY,
title VARCHAR NOT NULL,
text VARCHAR NOT NULL
)
CREATE TABLE IF NOT EXISTS comment (
id SERIAL PRIMARY KEY,
text VARCHAR NOT NULL,
post_id SERIAL REFERENCES post (id)
)
我希望能够查询这些表以便提供响应 看起来像这样:
{
"post" : [
{ id: 100,
title: "foo",
text: "foo foo",
comment: [1000,1001,1002] },
{ id: 101,
title: "bar",
text: "bar bar",
comment: [1003] }
],
"comment": [
{ id: 1000,
text: "bla blah foo",
post: 100 },
{ id: 1001,
text: "bla foo foo",
post: 100 },
{ id: 1002,
text: "foo foo foo",
post: 100 },
{ id: 1003,
text: "bla blah bar",
post: 101 },
]
}
天真地这样做会涉及到 SELECT
语句,
第一个是
SELECT DISTINCT ON(post.id), post.title, post.text, comment.id
FROM post, comment
WHERE post.id = comment.post_id
...第二个是
SELECT DISTINCT ON(comment.id), comment.text, post.id
FROM post, comment
WHERE post.id = comment.post_id
但是,我不禁想到有一种方法可以做到这一点
只有一个 SELECT
语句 - 这可能吗?
注意事项:
- 我正在使用 Postgres,但我不需要特定于 Postgres 的解决方案。任何标准 SQL 解决方案都应该可以。
- 上面的查询只是说明性的,它们并不能准确地告诉我们目前需要什么。
- 这里看起来天真的解决方案是在相同的两个表上执行相同的连接,只是每次在不同的表上执行不同的操作。这无疑留下了改进的余地。
- 看来 Rails 中的 ActiveModel 序列化程序 already do this - 如果有熟悉他们的人愿意对他们的幕后工作方式发表意见,那就太好了。
最佳答案
您需要两个查询来获取您布置的表单:
SELECT p.id, p.title, p.text, array_agg(c.id) AS comments
FROM post p
JOIN comment c ON c.post_id = p.id
WHERE p.id = ???
GROUP BY p.id;
或者更快,如果您真的想检索所有或大部分帖子:
SELECT p.id, p.title, p.text, c.comments
FROM post p
JOIN (
SELECT post_id, array_agg(c.id) AS comments
FROM comment
GROUP BY 1
) c ON c.post_id = p.id
GROUP BY 1;
加上:
SELECT id, text, post_id
FROM comment
WHERE post_id = ??;
单一查询
SQL 每次查询只能发送一个 结果类型。对于单个查询,您必须合并两个表,列出冗余的列以进行发布。这与您问题中的预期答复相冲突。您必须放弃两个相互冲突的要求之一。
SELECT p.id, p.title, p.text AS p_text, c.id, c.text AS c_text
FROM post p
JOIN comment c ON c.post_id = p.id
WHERE p.id = ???
另外:列 comment.post_id
应该是 integer
,而不是 serial
!此外,列名可能只是为了快速展示。您不会使用非描述性的 text
作为列名,这也与基本数据类型冲突。
比较这个相关案例:
关于sql - 使用一个 SELECT 而不是两个来服务于旁加载的 API 请求?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25205026/