这是“set-within-sets”查询的一个示例。最通用的方法是使用group by
并将逻辑放在having
子句中。对于您的情况:
SELECT B.name, B.author, B.id, B.finished, B.manual
FROM books B INNER JOIN
xrefbookstags XRBT
ON XRBT.idBooks = B.id JOIN
tags T
ON XRBT.idTags = T.id
group by B.name, B.author, B.id, B.finished, B.manual
having sum(case when t.name = 'novel' then 1 else 0 end) > 0 and
sum(case when t.name = 'biography' then 1 else 0 end) > 0 and
sum(case when t.name not in ('novel', 'biography') then 1 else 0 end) = 0;
逻辑如下。当至少一个标签为 时,第一个子句为真'novel'
。当至少一个子句为真时,第二个子句为真,当'biography'
没有其他标签时,第三个子句为真。
这很容易概括。如果您想要有这两个标签但可以有其他标签的书,只需省略第三个子句:
having sum(case when t.name = 'novel' then 1 else 0 end) > 0 and
sum(case when t.name = 'biography' then 1 else 0 end) > 0;
如果您想要具有其中一种的书籍:
having sum(case when t.name = 'novel' then 1 else 0 end) > 0 or
sum(case when t.name = 'biography' then 1 else 0 end) > 0;
如果您想要具有这两个加“历史”的书籍,那么您只需将其添加到:
having sum(case when t.name = 'novel' then 1 else 0 end) > 0 and
sum(case when t.name = 'biography' then 1 else 0 end) > 0 and
sum(case when t.name = 'historical' then 1 else 0 end) > 0;
而且,如果你想要那个但不是关于烹饪:
having sum(case when t.name = 'novel' then 1 else 0 end) > 0 and
sum(case when t.name = 'biography' then 1 else 0 end) > 0 and
sum(case when t.name = 'historical' then 1 else 0 end) > 0 and
sum(case when t.name = 'cooking' then 1 else 0 end) = 0;
编辑:
如果您有要匹配的以逗号分隔的标签列表,则可以执行以下操作:
having sum(case when ','+@List+',' not like '%,'+t.name+',%' then 1 else 0 end) = 0 and
count(distinct t.name) = 1 + len(@list) - len(replace(@list, ',', ''))
第一个子句说所有标签都在列表中。第二个说标签的长度是列表的长度。
这本质上是伪代码。不同的数据库对len()
函数有不同的名称,将字符串连接在一起的方式不同,表达变量值的方式也不同。但意图应该是明确的。