每个匹配的ASOC的SQL连接查询返回行

时间:2013-07-04 19:27:39

标签: sql join

假设有3个表:BOOKS,TAGS和ASOC

+----------+  +----------+  +----------+
|BOOKS     |  |TAGS      |  |ASOC      |
+----------+  +----------+  +----------+
|book_id   |  |tag_id    |  |book_id   |
|book_name |  |tag       |  |tag_id    |
|...       |  +----------+  +----------+
+----------+

希望这个例子中的使用/意图是显而易见的。

我想查询与某组标签相匹配的图书。所以我尝试了类似的东西:

SELECT B.book_name
FROM   BOOKS B
,      TAGS T
,      ASOC A
WHERE  B.book_id = A.book_id
AND    T.tag_id = A.tag_id
AND    (T.tag = 'Classic' OR T.tag = 'Fiction')

我得到的不良结果是每本书被多次列出,每个标签ASOC条目一次。我只想要匹配的独特书籍清单。我该怎么做?

提前致谢。

3 个答案:

答案 0 :(得分:0)

使用DISTINCT获取唯一列表:

SELECT DISTINCT B.book_name
FROM   BOOKS B
,      TAGS T
,      ASOC A
WHERE  B.book_id = A.book_id
AND    T.tag_id = A.tag_id
AND    (T.tag = 'Classic' OR T.tag = 'Fiction')

作为进一步的增强,可以重写最后一行,使其更具可读性:

AND    T.tag IN ('Classic', 'Fiction')

您还可以提高JOIN的效率:

SELECT DISTINCT B.book_name
FROM   BOOKS B
       INNER JOIN ASOC A on B.book_id = A.book_id
       INNER JOIN TAGS T on A.tag_id = T.tag_id
WHERE  T.tag IN ('Classic', 'Fiction')

答案 1 :(得分:0)

最简单的方法是将SELECT B.book_name更改为SELECT DISTINCT B.book_name;但是,写一下可能更好:

SELECT book_name
  FROM books
 WHERE book_id IN
        ( SELECT book_id
            FROM asoc
           WHERE tag_id IN
                  ( SELECT tag_id
                      FROM tags
                     WHERE tag IN ('Classic', 'Fiction')
                  )
        )
;

其中查询结构使您更清楚自己真正想要的内容。 (我建议尝试使用实际数据;有可能一个人的表现要比另一个好得多。)

答案 2 :(得分:0)

你说你想“匹配一组特定的标签”。如果我将其表示为“所有标记”,则这是“set-within-sets”查询。我喜欢使用聚合解决这些问题:

SELECT B.book_name
FROM   BOOKS B join
       ASOC A
       on B.book_id = A.book_id
       TAGS T
       on T.tag_id = A.tag_id
group by B.book_name
having sum(case when t.tag = 'Classic' then 1 else 0 end) > 0 and
       sum(case when t.tag = 'Fiction' then 1 else 0 end) > 0;

having子句计算每个标记的出现次数。比较要求两个标记都在集合中。

顺便说一下,如果您想要与 标记相匹配的图书,只需将and更改为or即可。我喜欢这种方法的原因是因为查询非常灵活。只需更改having子句即可表达许多不同的条件。

我还将您的查询更改为使用ANSI标准连接语法。您应该学会使用这种语法编写查询。