一对多 SQL SELECT 连接成单行

One-to-Many SQL SELECT concatenated into single row

我正在使用 Postgres,我有以下方案。

订单

| id |    status   |
|----|-------------|
|  1 |  delivered  |
|  2 | recollected | 

评论

| id |   text  | user | order |
|----|---------|------|-------|
|  1 | texto 1 |  10  |   20  |
|  2 | texto 2 |  20  |   20  |

因此,在这种情况下,一个订单可以有很多评论。

我需要遍历订单并得到如下内容:

| id |    status   |    comments    |
|----|-------------|----------------|
|  1 |  delivered  | text 1, text 2 |
|  2 | recollected |                |

我尝试使用 LEFT JOIN 但它不起作用

SELECT
    Order.id,
    Order.status,
    "Comment".text
FROM  "Order" 
LEFT JOIN "Comment" ON Order.id = "Comment"."order"

它returns这个:

| id |    status   | text   |
|----|-------------|--------|
|  1 |  delivered  | text 1 |
|  1 |  delivered  | text 2 |
|  2 |  recollected|        |

您快完成了 - 您只需要聚合:

SELECT
    o.id,
    o.status,
    STRING_AGG(c.text, ',') comments
FROM  "Order" o
LEFT JOIN "Comment" c ON p.id = c."order"
GROUP BY o.id, o.status

我强烈建议不要使用名为 order: 的 table(and/or 列),因为它与语言关键字冲突。我还建议尽可能避免使用带引号的标识符 - 它们使查询的编写时间更长,没有任何好处。

请注意,您还可以使用相关子查询:

SELECT
    o.id,
    o.status,
    (SELECT STRING_AGG(c.text, ',') FROM "Comment" c WHERE c."order" = p.id) comments
FROM  "Order" o

您可以使其与 LEFT JOIN 一起使用并在加入后聚合。但先聚合后加入通常更有效

如果涉及 "Comment" 中的大部分或所有行:

SELECT o.id, o.status, c.comments
FROM   "Order" o
LEFT   JOIN (
   SELECT "order" AS id, string_agg(text, ', ') AS comments
   FROM   "Comment"
   GROUP  BY 1
   )  c USING (id);

索引无关紧要,但无论如何都必须读取大多数行。

只有一小部分行(例如,如果您在 "Order" 上有选择性过滤器):

SELECT o.id, o.status, c.comments
FROM   "Order" o
LEFT   JOIN LATERAL (
   SELECT string_agg(text, ', ')  AS comments
   FROM   "Comment"
   WHERE  "order" = o.id
   )  c ON true
WHERE  <some_selective_filter>;

在这种情况下,一定要在 ("Comment"."order") 上有一个 index,或者更专业的覆盖索引,包括 text:

 CREATE INDEX foo ON "Comment" ("order") INCLUDE (text);

相关:

旁白:考虑在 Postgres 中合法的、lower-case、不带引号的标识符。特别是,不要(滥用)使用完全保留的 SQL keywords like ORDER 作为标识符。更清晰,更不容易出现偷偷摸摸的错误。参见:

  • Are PostgreSQL column names case-sensitive?