使用一个SELECT而不是两个来提供侧载API请求?

时间:2014-08-08 13:47:22

标签: sql performance postgresql select join

假设我使用以下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 - 如果有人和他们一起熟悉他们想知道他们如何在幕后工作,那就太好了。

2 个答案:

答案 0 :(得分:1)

您需要两个查询才能获得您制作的表单:

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用作列名,这也与基本数据类型冲突。 比较这个相关案例:

答案 1 :(得分:1)

  

但是,我不禁认为有一种方法可以只涉及一个SELECT语句 - 这可能吗?

技术上:是的。如果您真的想要json中的数据,可以使用PostgreSQL(9.2+)使用json functions生成数据,如:

SELECT row_to_json(sq)
FROM (
  SELECT array_to_json(ARRAY(
           SELECT row_to_json(p)
           FROM (
             SELECT *, ARRAY(SELECT id FROM comment WHERE post_id = post.id) AS comment
             FROM post
           ) AS p
         )) AS post,
         array_to_json(ARRAY(
           SELECT row_to_json(comment)
           FROM comment
         )) AS comment
) sq;

但我不确定它是否值得 - 在没有限制/分页的情况下转储所有数据通常不是一个好主意。

SQLFiddle