SQL JOIN 查询中的重复条目

Repetitive entries in SQL JOIN query

我有三个table帖子评论用户 . posts table 包括两种类型的帖子,questionanswer。对问题和答案进行评论。我的目的是获得一个带有评论、答案和答案评论的问题。我还需要用户 table 的用户名作为我获取的每个问题、答案和评论的作者。我正在使用 Postgres 9.5,并使用 json_agg() 函数。

虽然我需要的示例输出应该类似于以下第一个,但我得到了重复的条目。

我在这里缺少什么?正确的 group by 子句可能是。或者用他们的评论收集答案的子查询不是这样做的方法。当我从评论 table 的帖子中注释掉左连接时,我得到了想要的结果而没有对问题发表评论。此外,当我取消包含子查询的左连接时,我得到了预期的非重复结果,而且这也不是我想要的完整数据集。这些是我到目前为止为解决问题而收集的东西。

我需要的:

[
  {
    "post_id": "10",
    "created_at": "2016-05-10T00:16:54.469Z",
    "post_type": "question",
    "post_title": "qwerty",
    "post_text": "asdasd asda sdasd",
    "post_author_id": 1,
    "author": "isikfsc",
    "parent_post_id": null,
    "is_accepted": null,
    "acceptor_id": null,
    "answers": [
      {
        "post_id": 17,
        "created_at": "2016-05-10T04:58:56.350229",
        "post_type": "answer",
        "post_title": null,
        "post_text": "222asda dasdad asdada",
        "post_author_id": 1,
        "author": "isikfsc",
        "parent_post_id": 10,
        "is_accepted": null,
        "acceptor_id": null,
        "comments": [
          {
            "id": 5,
            "created_at": "2016-05-10T10:56:30.220128",
            "text": "qweqwe",
            "author_id": 1,
            "author": "isikfsc",
            "parent_post_id": 17
          },
          {
            "id": 8,
            "created_at": "2016-05-10T11:00:00.182991",
            "text": "sasasd",
            "author_id": 1,
            "author": "isikfsc",
            "parent_post_id": 17
          }
        ]
      },
      {
        "post_id": 14,
        "created_at": "2016-05-10T04:19:19.005556",
        "post_type": "answer",
        "post_title": null,
        "post_text": "asdasdasdasd",
        "post_author_id": 1,
        "author": "isikfsc",
        "parent_post_id": 10,
        "is_accepted": null,
        "acceptor_id": null,
        "comments": [
          {
            "id": 2,
            "created_at": "2016-05-10T05:25:34.671008",
            "text": "qeqweqwe",
            "author_id": 1,
            "author": "isikfsc",
            "parent_post_id": 14
          }
        ]
      }
    ],
    "comments": [
        {
          "id": 1,
          "created_at": "2016-05-10T10:56:30.220128",
          "text": "qweqwe",
          "author_id": 1,
          "author": "isikfsc",
          "parent_post_id": 10
        },
        {
          "id": 4,
          "created_at": "2016-05-10T11:00:00.182991",
          "text": "sasasd",
          "author_id": 1,
          "author": "isikfsc",
          "parent_post_id": 10
        }
    ]
  }
]

我的查询是:

SELECT
    q.*,
    json_agg(ac.*) AS answers,
    json_agg(c.*) AS comments --comments on posts of post_id questions
FROM posts q

LEFT JOIN 
    (
        SELECT
            a.*,
            json_agg(c.*) AS comments -- comments on posts of post_id answers
        FROM posts a
        LEFT JOIN comments c
        ON a.post_id = c.parent_post_id

        GROUP BY a.post_id
    ) ac
ON q.post_id = ac.parent_post_id

LEFT JOIN comments c
ON q.post_id = c.parent_post_id

WHERE q.post_id = 10
GROUP BY q.post_id

我得到的:

[
  {
    "post_id": "10",
    "created_at": "2016-05-10T00:16:54.469Z",
    "post_type": "question",
    "post_title": "qwerty",
    "post_text": "asdasd asda sdasd",
    "post_author_id": 1,
    "parent_post_id": null,
    "is_accepted": null,
    "acceptor_id": null,
    "answers": [
      {
        "post_id": 17,
        "created_at": "2016-05-10T04:58:56.350229",
        "post_type": "answer",
        "post_title": null,
        "post_text": "222asda dasdad asdada",
        "post_author_id": 1,
        "parent_post_id": 10,
        "is_accepted": null,
        "acceptor_id": null,
        "comments": [
          {
            "id": 5,
            "created_at": "2016-05-10T10:56:30.220128",
            "text": "qweqwe",
            "author_id": 1,
            "parent_post_id": 17
          },
          {
            "id": 8,
            "created_at": "2016-05-10T11:00:00.182991",
            "text": "sasasd",
            "author_id": 1,
            "parent_post_id": 17
          }
        ]
      },
      {
        "post_id": 17,
        "created_at": "2016-05-10T04:58:56.350229",
        "post_type": "answer",
        "post_title": null,
        "post_text": "222asda dasdad asdada",
        "post_author_id": 1,
        "parent_post_id": 10,
        "is_accepted": null,
        "acceptor_id": null,
        "comments": [
          {
            "id": 5,
            "created_at": "2016-05-10T10:56:30.220128",
            "text": "qweqwe",
            "author_id": 1,
            "parent_post_id": 17
          },
          {
            "id": 8,
            "created_at": "2016-05-10T11:00:00.182991",
            "text": "sasasd",
            "author_id": 1,
            "parent_post_id": 17
          }
        ]
      },
      {
        "post_id": 17,
        "created_at": "2016-05-10T04:58:56.350229",
        "post_type": "answer",
        "post_title": null,
        "post_text": "222asda dasdad asdada",
        "post_author_id": 1,
        "parent_post_id": 10,
        "is_accepted": null,
        "acceptor_id": null,
        "comments": [
          {
            "id": 5,
            "created_at": "2016-05-10T10:56:30.220128",
            "text": "qweqwe",
            "author_id": 1,
            "parent_post_id": 17
          },
          {
            "id": 8,
            "created_at": "2016-05-10T11:00:00.182991",
            "text": "sasasd",
            "author_id": 1,
            "parent_post_id": 17
          }
        ]
      },
      {
        "post_id": 17,
        "created_at": "2016-05-10T04:58:56.350229",
        "post_type": "answer",
        "post_title": null,
        "post_text": "222asda dasdad asdada",
        "post_author_id": 1,
        "parent_post_id": 10,
        "is_accepted": null,
        "acceptor_id": null,
        "comments": [
          {
            "id": 5,
            "created_at": "2016-05-10T10:56:30.220128",
            "text": "qweqwe",
            "author_id": 1,
            "parent_post_id": 17
          },
          {
            "id": 8,
            "created_at": "2016-05-10T11:00:00.182991",
            "text": "sasasd",
            "author_id": 1,
            "parent_post_id": 17
          }
        ]
      },
      {
        "post_id": 14,
        "created_at": "2016-05-10T04:19:19.005556",
        "post_type": "answer",
        "post_title": null,
        "post_text": "asdasdasdasd",
        "post_author_id": 1,
        "parent_post_id": 10,
        "is_accepted": null,
        "acceptor_id": null,
        "comments": [
          {
            "id": 2,
            "created_at": "2016-05-10T05:25:34.671008",
            "text": "qeqweqwe",
            "author_id": 1,
            "parent_post_id": 14
          }
        ]
      },
      {
        "post_id": 14,
        "created_at": "2016-05-10T04:19:19.005556",
        "post_type": "answer",
        "post_title": null,
        "post_text": "asdasdasdasd",
        "post_author_id": 1,
        "parent_post_id": 10,
        "is_accepted": null,
        "acceptor_id": null,
        "comments": [
          {
            "id": 2,
            "created_at": "2016-05-10T05:25:34.671008",
            "text": "qeqweqwe",
            "author_id": 1,
            "parent_post_id": 14
          }
        ]
      },
      {
        "post_id": 14,
        "created_at": "2016-05-10T04:19:19.005556",
        "post_type": "answer",
        "post_title": null,
        "post_text": "asdasdasdasd",
        "post_author_id": 1,
        "parent_post_id": 10,
        "is_accepted": null,
        "acceptor_id": null,
        "comments": [
          {
            "id": 2,
            "created_at": "2016-05-10T05:25:34.671008",
            "text": "qeqweqwe",
            "author_id": 1,
            "parent_post_id": 14
          }
        ]
      },
      {
        "post_id": 14,
        "created_at": "2016-05-10T04:19:19.005556",
        "post_type": "answer",
        "post_title": null,
        "post_text": "asdasdasdasd",
        "post_author_id": 1,
        "parent_post_id": 10,
        "is_accepted": null,
        "acceptor_id": null,
        "comments": [
          {
            "id": 2,
            "created_at": "2016-05-10T05:25:34.671008",
            "text": "qeqweqwe",
            "author_id": 1,
            "parent_post_id": 14
          }
        ]
      }
    ],
    "comments": [
      {
        "id": 1,
        "created_at": "2016-05-10T05:25:28.200327",
        "text": "asadasdad",
        "author_id": 1,
        "parent_post_id": 10
      },
      {
        "id": 4,
        "created_at": "2016-05-10T10:25:23.381177",
        "text": "werwer",
        "author_id": 1,
        "parent_post_id": 10
      },
      {
        "id": 1,
        "created_at": "2016-05-10T05:25:28.200327",
        "text": "asadasdad",
        "author_id": 1,
        "parent_post_id": 10
      },
      {
        "id": 4,
        "created_at": "2016-05-10T10:25:23.381177",
        "text": "werwer",
        "author_id": 1,
        "parent_post_id": 10
      },
      {
        "id": 1,
        "created_at": "2016-05-10T05:25:28.200327",
        "text": "asadasdad",
        "author_id": 1,
        "parent_post_id": 10
      },
      {
        "id": 4,
        "created_at": "2016-05-10T10:25:23.381177",
        "text": "werwer",
        "author_id": 1,
        "parent_post_id": 10
      },
      {
        "id": 1,
        "created_at": "2016-05-10T05:25:28.200327",
        "text": "asadasdad",
        "author_id": 1,
        "parent_post_id": 10
      },
      {
        "id": 4,
        "created_at": "2016-05-10T10:25:23.381177",
        "text": "werwer",
        "author_id": 1,
        "parent_post_id": 10
      }
    ]
  }
]

所有参与方都加入后就会进行分组,因此聚合将取决于生成的基数。加入带有答案和评论的帖子会导致它们之间的完全连接,从而复制所有值。它们需要分开并单独执行,一种方法如下:

SELECT
    q.*,
    (SELECT json_agg(ac.*)
     FROM (
       SELECT a.*, json_agg(c.*) AS comments
       FROM posts a
       LEFT JOIN comments c ON (a.post_id = c.parent_post_id)
       WHERE a.parent_post_id = q.post_id
       GROUP BY a.post_id
       ) ac
    ) AS answers,
    json_agg(c.*) AS comments --comments on posts of post_id questions
FROM posts q
LEFT JOIN comments c ON (q.post_id = c.parent_post_id)
WHERE q.post_id = 10
GROUP BY q.post_id;

或者:

SELECT q.*, qa.answers, qc.comments
FROM posts q
LEFT JOIN (
  SELECT ac.parent_post_id, json_agg(ac.*) AS answers
  FROM (
    SELECT ac.*, json_agg(c.*) AS comments
    FROM posts ac
    LEFT JOIN comments c ON (c.parent_post_id = ac.post_id)
    GROUP BY ac.post_id
    ) ac
  GROUP BY ac.parent_post_id
  ) qa ON (qa.parent_post_id = q.post_id)
LEFT JOIN (
  SELECT c.parent_post_id, json_agg(c.*) AS comments
  FROM comments c
  GROUP BY c.parent_post_id
  ) qc ON (qc.parent_post_id = q.post_id)
WHERE q.post_id = 10;