基本集合论思想的SQL解决方案

基本集合论思想的SQL解决方案,sql,select,Sql,Select,我有一个数据库表,如下所示: ╔═════════════════╦════════════╗ ║ ADVERTISEMENTID ║ CATEGORYID ║ ╠═════════════════╬════════════╣ ║ 1 ║ A ║ ║ 1 ║ C ║ ║ 2 ║ A ║ ╚═════════════════╩════════════╝ 这基本

我有一个数据库表,如下所示:

╔═════════════════╦════════════╗
║ ADVERTISEMENTID ║ CATEGORYID ║
╠═════════════════╬════════════╣
║               1 ║ A          ║
║               1 ║ C          ║
║               2 ║ A          ║
╚═════════════════╩════════════╝
这基本上意味着:

  • 广告#1分为两类:AC,以及
  • 广告属于一个类别:A
假设用户传递的参数可能是(a、B、C)的类别。这里两个广告匹配,因为可能的类别集包含所有广告#1的类别,而可能的类别集包含所有广告#2的类别

但是,如果用户将另一组可能的类别作为参数传递,例如(a,D)。这里只有广告#2匹配,而广告#1不匹配,因为可能的类别集不包含所有#1的类别

现在我不知道如何用SQL来表达这一点,也就是说,在给定可能的类别ID参数的情况下,构建一个SQL查询,从表中检索不同的广告ID


有人能帮忙吗?

这个问题有很多可能的解决方案,但我使用的是在
HAVING
子句中过滤结果

SELECT  advertisementID
FROM    TableName
GROUP   BY advertisementID
HAVING  SUM(CASE WHEN CategoryID IN ('A','B','C') THEN 1 ELSE 0 END) > 0 AND
        SUM(CASE WHEN CategoryID NOT IN ('A','B','C') THEN 1 ELSE 0 END) = 0
简要说明

SUM(CASE WHEN CategoryID IN ('A','B','C') THEN 1 ELSE 0 END) > 0
它所做的是计算与给定列表匹配的
类别ID
。它应该至少具有列表中的一个匹配项。另一个,

SUM(CASE WHEN CategoryID NOT IN ('A','B','C') THEN 1 ELSE 0 END) = 0

它统计给定列表中不匹配的所有
类别ID
。这一次,该值应为零,以便对结果进行过滤。

使用SQLFIDLE中@JW的模式,另一种解决方案是:

SELECT matchacat.advertisementID
FROM   (select distinct advertisementID
        from   TableName
        where  CategoryID in ('A', 'D')) AS matchacat
LEFT JOIN
      (select distinct advertisementID
      from   TableName
      where  not CategoryID in ('A', 'D'))AS notmatch
ON    (matchacat.advertisementID = notmatch.advertisementID)
WHERE notmatch.advertisementID is null

因此,获取至少与一个cat匹配的ads集,然后获取具有不匹配cat的ads集,并使用外部联接从第一个集合中删除第二个集合。

这就是所谓的集合内集合问题。我认为找到任何类别匹配的最佳方法是以下方法:

select ADVERTISEMENTID
from t
group by ADVERTISEMENTID
having sum(case when categoryid = 'A' then 1 else 0 end) > 0 or
       sum(case when categoryid = 'B' then 1 else 0 end) > 0 or
       sum(case when categoryid = 'C' then 1 else 0 end) > 0
换句话说,这是通过
advertisementid
进行聚合,并对每个类别值进行单独比较。
sum()。
表示其中任何一项都必须为真

对于子集关系,我再添加一个子句来计算不匹配项:

select ADVERTISEMENTID
from t
group by ADVERTISEMENTID
having (sum(case when categoryid = 'A' then 1 else 0 end) > 0 or
        sum(case when categoryid = 'B' then 1 else 0 end) > 0 or
        sum(case when categoryid = 'C' then 1 else 0 end) > 0
       ) and
       sum(case when categoryid in ('A', 'B', 'C') then 0 else 1 end) = 0
我喜欢这种方法的原因是因为它很有表现力。如果我们将
更改为
,则我们要求所有三个类别:

select ADVERTISEMENTID
from t
group by ADVERTISEMENTID
having sum(case when categoryid = 'A' then 1 else 0 end) > 0 and
       sum(case when categoryid = 'B' then 1 else 0 end) > 0 and
       sum(case when categoryid = 'C' then 1 else 0 end) > 0
如果我们想从集合中至少找到两个匹配项,我们可以添加
count(distinct)


等等。

非常感谢您的详细回复JW!实际上,我正在寻找一些更简单的东西,可以移植到JPQL(Java Persistence API)。你能简单地描述一下你暗示的其他可能的解决方案吗?@balteo其他解决方案正在使用
EXISTS
JOIN
。我能知道我的答案中缺少什么吗?我的意思是,不接受它没有什么错,但也许我需要一些解释,这样我才能改进我的答案。我也在考虑这个问题,但您显示的第一个查询的问题是,它应该在列表上显示所有的
类别ID
(我认为,通过理解给出的示例,用户不希望看到)这与
从t中选择ADVERTISEMENTID非常相似,其中CategoryID在('A','B','C')组中,通过ADVERTISEMENTID具有COUNT(DISTINCT CategoryID)=3,对吗?@JW。非常感谢。问题是寻找一个严格的子集关系。呵呵,也许OP没有更好地解释主题
+1
select ADVERTISEMENTID
from t
group by ADVERTISEMENTID
having (sum(case when categoryid = 'A' then 1 else 0 end) > 0 or
        sum(case when categoryid = 'B' then 1 else 0 end) > 0 or
        sum(case when categoryid = 'C' then 1 else 0 end) > 0
       ) and
       count(distinct categoryid) >= 2