一对多 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);
相关:
- Concatenate multiple result rows of one column into one, group by another column
- Multiple array_agg() calls in a single query
- Does a query with a primary key and foreign keys run faster than a query with just primary keys?
旁白:考虑在 Postgres 中合法的、lower-case、不带引号的标识符。特别是,不要(滥用)使用完全保留的 SQL keywords like ORDER
作为标识符。更清晰,更不容易出现偷偷摸摸的错误。参见:
- Are PostgreSQL column names case-sensitive?
我正在使用 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);
相关:
- Concatenate multiple result rows of one column into one, group by another column
- Multiple array_agg() calls in a single query
- Does a query with a primary key and foreign keys run faster than a query with just primary keys?
旁白:考虑在 Postgres 中合法的、lower-case、不带引号的标识符。特别是,不要(滥用)使用完全保留的 SQL keywords like ORDER
作为标识符。更清晰,更不容易出现偷偷摸摸的错误。参见:
- Are PostgreSQL column names case-sensitive?