Postgres - 多个连接导致我的查询返回不正确的数据

时间:2011-06-25 01:22:50

标签: sql postgresql join

我的数据库中有三个表。他们的架构基本上是:

books:
id | book_title

books_tags:
id | book_id | tag_id

books_votes:
id | book_id | vote

这个想法是能够搜索书籍并按给定的标签过滤(在本例中为716和101)。 total_votes用于ORDER BY caluse。

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子句中添加任意数量的标签ID,它会继续过滤结果,只显示带有这些标签的书籍。完美。

当我将第二个JOIN添加到books_votes表时,会出现问题。此连接不会产生任何错误,只会导致查询返回错误的数据 - 就像它忽略标记ID一样。

第二次加入有什么问题?

编辑:

这是表中的转储:

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册已使用ID 716和101标记。

这是我在连接books_votes表时运行查询时返回的内容:

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

2 个答案:

答案 0 :(得分:4)

逐步构建复杂的SQL。

这为您提供了具有所需标签的书籍。它只与表定义一样可靠。您的表定义不应允许一本书具有两次相同的标记。你需要一个UNIQUE约束(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

您的版本不起作用,因为它返回了错误的图书ID号。 books_votes上的JOIN和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

答案 1 :(得分:0)

据我了解,您需要所有带有标签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