Postgres - множественные объединения приводят к тому, что мой запрос возвращает неверные данные - PullRequest
1 голос
/ 25 июня 2011

У меня есть три таблицы в моей базе данных.Их схема в основном:

books:
id | book_title

books_tags:
id | book_id | tag_id

books_votes:
id | book_id | vote

Идея состоит в том, чтобы иметь возможность искать книги и фильтровать по заданным тегам (в данном случае 716 и 101).total_votes используется в календаре ORDER BY.

SELECT
    books.id AS books_id, sum(book_votes.vote) AS total_votes 
FROM
    books
JOIN
    -- works fine without this join
    book_votes ON books.id = book_votes.book_id
JOIN
    books_tags ON books.id = books_tags.book_id
WHERE
    books_tags.tag_id IN (716, 101)
GROUP BY
    books.id 
HAVING
    count(books.id) = 2

Фильтрация тегов сама по себе прекрасно работает.Я могу добавить столько идентификаторов тегов к предложению IN, сколько захочу, и он продолжит фильтровать результаты, чтобы показывать только книги с этими тегами.Идеальный.

Проблема возникает, когда я добавляю второй JOIN к таблице books_votes.Это объединение не приводит к ошибкам, оно просто заставляет запрос возвращать неверные данные - как если бы оно игнорировало идентификаторы тегов.

Что плохого во втором соединении?

РЕДАКТИРОВАТЬ:

Вот дампы из таблиц:

books:
 id |   book_title  
----+-----------------
  1 | first
  2 | second
  3 | third book
  4 | fourth book
  5 | fifth
  6 | sixth book

books_tags:
 id | book_id | tag_id 
----+---------+--------
  1 |       1 |    293
  2 |       1 |     32
  3 |       1 |    370
  4 |       2 |    101
  5 |       2 |    357
  6 |       3 |    554
  7 |       3 |    808
  8 |       3 |    716
  9 |       3 |    101
 10 |       4 |    787
 11 |       4 |    808
 12 |       4 |    322
 13 |       5 |    787
 17 |       6 |    716
 18 |       6 |    554
 19 |       6 |    101

books_votes:
 id | book_id | vote 
----+---------+------
  2 |       2 |    1
  3 |       3 |    1
  4 |       4 |    1
  7 |       4 |    1
  8 |       2 |    1
 11 |       5 |    1
 12 |       5 |    1
 13 |       1 |    1

Вот данные, возвращенные из отправленного мной запроса, когда я пропускаю второе объединение (для books_votes):

 book_id 
---------
   6
   3

Как видите, верные книги возвращаются.Книги 6 и 3 помечены идентификаторами 716 и 101.

Вот что возвращается, когда я запускаю запрос с присоединенной таблицей books_votes:

 book_id | total_votes 
---------+-------------
    3    |    2
    2    |    3

Ответы [ 2 ]

4 голосов
/ 25 июня 2011

Построение сложного SQL шаг за шагом.

Это дает вам книги, которые имеют оба обязательных тега. Это так же надежно, как и определение вашей таблицы. Ваше определение таблицы не должно позволять одной книге иметь один и тот же тег дважды. Вам нужно УНИКАЛЬНОЕ ограничение для (book_id, tag_id).

SELECT book_id 
FROM books_tags
WHERE books_tags.tag_id IN (716, 101)
GROUP BY book_id
HAVING COUNT(tag_id) = 2

book_id
--
6
3

Вы можете использовать это в JOIN.

SELECT books.id
FROM books
INNER JOIN (
    SELECT book_id 
    FROM books_tags
    WHERE books_tags.tag_id IN (716, 101)
    GROUP BY book_id
    HAVING COUNT(tag_id) = 2) bt ON bt.book_id = books.id

book_id
--
6
3

Присоединение к таблице голосов должно отбрасывать book_id 6 из результата. (Нет голосов за 6.)

SELECT books.id
FROM books
INNER JOIN (
    SELECT book_id 
    FROM books_tags
    WHERE books_tags.tag_id IN (716, 101)
    GROUP BY book_id
    HAVING COUNT(tag_id) = 2) bt ON bt.book_id = books.id
INNER JOIN books_votes bv ON bv.book_id = books.id

book_id
--
3

Теперь вы можете добавить столбец голосования к запросу.

SELECT books.id, bv.vote
FROM books
INNER JOIN (
    SELECT book_id 
        FROM books_tags
    WHERE books_tags.tag_id IN (716, 101)
    GROUP BY book_id
    HAVING COUNT(tag_id) = 2) bt ON bt.book_id = books.id
INNER JOIN books_votes bv ON bv.book_id = books.id

book_id  vote
--
3        1

Наконец, вы можете суммировать голоса.

SELECT books.id, SUM(bv.vote) AS total_votes
FROM books
INNER JOIN (
    SELECT book_id 
        FROM books_tags
    WHERE books_tags.tag_id IN (716, 101)
    GROUP BY book_id
    HAVING COUNT(tag_id) = 2) bt ON bt.book_id = books.id
INNER JOIN books_votes bv ON bv.book_id = books.id
GROUP BY books.id;

book_id  total_votes
--
3        1

Ваша версия не работает, потому что она возвращает неправильные номера идентификаторов книг. Комбинация JOIN on books_votes и предложения WHERE не дает ожидаемого результата.

SELECT books.id AS books_id
FROM books
JOIN books_votes ON books.id = books_votes.book_id
JOIN books_tags ON books.id = books_tags.book_id
WHERE books_tags.tag_id IN (716, 101)
GROUP BY books.id 

books_id
--
3
2

Книга 2 включена не потому, что в ней есть оба тега, а потому, что у нее два голоса.

SELECT books.id AS books_id, books_tags.tag_id, books_votes.vote
FROM books
JOIN books_votes ON books.id = books_votes.book_id
JOIN books_tags ON books.id = books_tags.book_id
WHERE books_tags.tag_id IN (716, 101)
ORDER BY books_id, tag_id

book_id  tag_id     vote
--
2        101        1
2        101        1
3        101        1
3        716        1
0 голосов
/ 25 июня 2011

Насколько я понимаю, вам нужны все книги, имеющие теги 716 и 101, и вам нужно подсчет голосов для каждой книги.

select *,
    (select count(*) from book_votes as vts where vts.book_id = bks.id) as vote_count
from books as bks
where 
    id in 
    (
        select book_id
        from books_tags as tgs
        where tgs.tag_id in (716, 101)
        group by book_id
        having count(*) = 2
    )

Результат:

id          book_title      vote_count
----------- --------------- -----------
3           third book      1
6           sixth book      0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...