假设有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条目一次。我只想要匹配的独特书籍清单。我该怎么做?
提前致谢。
答案 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标准连接语法。您应该学会使用这种语法编写查询。