Один ко многим SQL SELECT объединены в одну строку - PullRequest
0 голосов
/ 03 августа 2020

Я использую 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"

, он возвращает это:

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

Ответы [ 2 ]

2 голосов
/ 04 августа 2020

Почти готово - вам просто нужно агрегирование:

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
1 голос
/ 04 августа 2020

Вы можете заставить его работать с 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, такие как ORDER, в качестве идентификатора. Намного яснее и меньше возможностей для скрытых ошибок. См .:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...