Выберите данные из одной таблицы на основе выбранных строк в другой - PullRequest
1 голос
/ 09 марта 2019

У меня есть три таблицы:

книга:

{id, title, status}
{1, SuperHero, false}
{2, Hobbit, true}
{3, Marvel, true}

теги:

{id, name}
{1, Drama}
{2, Comedy}
{3, Triller}

books_tags:

{book_id, tag_id}
{1, 1}
{1, 2}
{2, 2}

Каждая книга может иметь или не иметь много уникальных для нее тегов.

1) Как правильно получить все теги с данными (tag.id, name) для одной книги в одном запросе на основе book_id?

2) Как правильно получить все книги с заголовком по статусу (true) с массивом тегов для каждой строки книги в одном запросе?

Я придумываю это для первого, но борюсь со вторым:

SELECT tag_id, name 
FROM books_tags 
    INNER JOIN tags ON tag_id = tags.id 
WHERE book_id = 1

Ответы [ 2 ]

1 голос
/ 09 марта 2019

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

Рассмотрим следующую настройку:

create temp table if not exists tags(
    id   int, 
    name text
);
insert into tags(id, name)
values  (1, 'Drama')
       ,(2, 'Comedy')
       ,(3, 'Thriller');

create temp table if not exists books(
    id        int, 
    title     text, 
    status    bool,
    book_tags int[]
);
insert into books(id, title, status, book_tags)
values  (1, 'SuperHero', false, array[1, 2])
       ,(2, 'Hobbit',    true,  array[2])
       ,(3, 'Marvel',    true,  null);

Теперь вы можете легко выполнять ваши запросы. например 1)

select  book_tags
from    books B 
where   B.id = 1;

Если вы хотите, чтобы теги были в отдельных строках, используйте функцию unnest(), например,

select  unnest(book_tags)
from    books B 
where   B.id = 1;

и 2) найти все книги с tag in [2] и status = true

select  id, title, book_tags
from    books B 
where   B.status = true 
    and B.book_tags @> array[2]  -- set query tags in on right side

В вашем примере данных для книги 1 указано status=false, поэтому array[1, 2] не вернет никаких результатов с этими данными. Поэтому я установил в своем примере использование только одного тега.

1 голос
/ 09 марта 2019

Для второго требования используйте следующее:

SELECT
    b.title,
    array_agg(t.name) AS tags
FROM books AS b
INNER JOIN books_tags AS bt ON (b.id = bt.book_id)
INNER JOIN tags AS t ON (bt.tag_id = t.id)
WHERE b.status = true
GROUP BY 1;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...