【问题标题】:One-to-Many SQL SELECT concatenated into single row一对多 SQL SELECT 连接成单行
【发布时间】:2020-11-23 22:59:08
【问题描述】:

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

订单

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

评论

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

所以,在这种情况下,一个订单可以有多个 cmets。

我需要遍历订单并得到类似的东西:

| 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"

它返回这个:

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

【问题讨论】:

    标签: sql string postgresql subquery string-aggregation


    【解决方案1】:

    您可以使其与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") 上拥有一个索引,或更专业的覆盖索引,包括text

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

    相关:

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

    【讨论】:

      【解决方案2】:

      你快到了 - 你只需要聚合:

      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 的表(和/或列):因为它与语言关键字冲突。我还建议尽可能避免使用带引号的标识符 - 它们会使查询的编写时间更长,没有任何好处。

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

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

      【讨论】:

        猜你喜欢
        • 2012-07-11
        • 1970-01-01
        • 1970-01-01
        • 2021-05-06
        • 2023-04-03
        • 1970-01-01
        • 2020-11-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多