Множественный случай и условия

У меня есть две такие таблицы:

идентификатор_коллекции добавлено_on edited_on 1 2024-06-19 20:16:41 нулевой 2 2024-06-19 20:17:42 нулевой 3 2024-06-19 20:17:57 нулевой

и

Detail_id коллекция тип_детали подробное_значение 1 1 заголовок Название книги 1 2 1 автор Джон Доу 3 1 издатель Издатель А 4 2 заголовок Название книги 2 5 2 автор Джейн Доу 6 3 заголовок Название книги 3 7 3 автор Джон Доу 8 3 издатель Издатель Б

Первая таблица — это основная таблица для хранения идентификатора моей коллекции книг, а вторая таблица — это сведения о коллекции с «коллекцией» внешнего ключа, связанной с первой таблицей.

Я хотел узнать название и автора каждой книги, поэтому попробовал:

select 
    collection_id, 
    (case when detail_type = 'title' then detail_value end) as title, 
    (case when detail_type = 'author' then detail_value end) as author
from 
    collections
left join 
    collection_details on collections.collection_id = collection_details.collection
where 
    detail_type = 'title' or detail_type = 'author';

Результат:

идентификатор_коллекции заголовок автор 1 Название книги 1 [нулевой] 1 [нулевой] Джон Доу 2 Название книги 2 [нулевой] 2 [нулевой] Джейн Доу 3 Название книги 3 [нулевой] 3 [нулевой] Джон Доу

На самом деле я ожидал:

идентификатор_коллекции заголовок автор 1 Название книги 1 Джон Доу 2 Название книги 2 Джейн Доу 3 Название книги 3 Джон Доу

Как я могу получить желаемый результат только с помощью одного запроса?

Использование conditional aggregation путем переноса в max() — один из вариантов. Я считаю, что у postgres тоже может быть опция filter.

Isolated 19.06.2024 16:03
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
1
55
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Одним из вариантов может быть использование предложения FILTER.

SELECT  c.collection_id,
        MAX(cd.detail_value) FILTER (WHERE cd.detail_type = 'title') AS title,
        MAX(cd.detail_value) FILTER (WHERE cd.detail_type = 'author') AS author
FROM collections c
LEFT JOIN collection_details cd ON c.collection_id = cd.collection 
GROUP BY c.collection_id
ORDER BY c.collection_id asc;

В качестве альтернативы вы можете использовать подзапрос, чтобы получить автора и название книги.

SELECT collection_id, book_author,book_title
FROM collections c
LEFT JOIN  (SELECT collection,
                  MAX(CASE WHEN detail_type = 'author' THEN detail_value END) AS book_author ,
                  MAX(CASE WHEN detail_type = 'title' THEN detail_value END) AS book_title 
           FROM  collection_details 
           GROUP BY collection 
         ) cd on cd.collection=c.collection_id
ORDER BY collection_id asc;

Смотрите пример

Я только что понял, что то, что я опубликовал, во многом совпадает с первым примером здесь, только без группировки + агрегирования + фильтрации без видимой причины. filter ничего не делает, поскольку строки были предварительно отфильтрованы по идентичному условию соединения — я думаю, @Isolated разыграл вас всей этой идеей. Если бы у одной коллекции могло быть несколько авторов/названий, вы бы, скорее всего, array_agg() указали здесь, а не только одного.

Zegarek 19.06.2024 21:37

Я говорил об этом выше: «Если бы одна коллекция могла иметь несколько авторов/названий, вы, скорее всего, использовали бы здесь array_agg(), а не показывали только одного». filter здесь ничего не делает, потому что вы уже указали AND cdTitle.detail_type = 'title' при его вставке, поэтому выполнение FILTER (WHERE cdTitle.detail_type = 'title') не имеет никакого эффекта — к этому моменту все не-заголовки уже исчезли из cdTitle. См. dbfiddle.uk/tm6F_CT7

Zegarek 19.06.2024 22:25

ПРИСОЕДИНЯЙТЕСЬ к collection_details дважды: демо на db<>fiddle

select c.collection_id
      ,t.detail_value as title
      ,a.detail_value as author
from collections as c
left join collection_details as t 
  on c.collection_id = t.collection and t.detail_type='title'
left join collection_details as a
  on c.collection_id = a.collection and a.detail_type='author';
идентификатор_коллекции заголовок автор 1 Название книги 1 Джон Доу 2 Название книги 2 Джейн Доу 3 Название книги 3 Джон Доу

Используйте разные псевдонимы и укажите нужный detail_type в качестве дополнительного join условия.


Если вы разрешите несколько авторов и названий в каждой коллекции, возможно, вы захотите получить string_agg()

select c.collection_id
      ,string_agg(distinct t.detail_value,', ') as titles
      ,string_agg(distinct a.detail_value,', ') as authors
from collections as c
left join collection_details as t 
  on c.collection_id = t.collection and t.detail_type='title'
left join collection_details as a
  on c.collection_id = a.collection and a.detail_type='author'
group by 1;
идентификатор_коллекции титулы авторы 1 Название книги 1 Джон Доу-младший, Джон Доу-старший 2 Название книги 2 Джейн Доу 3 Название книги 3 Джон Доу

Другие вопросы по теме