【问题标题】:SQL where clause with seperataed IN expression to implement CNF带有分隔 IN 表达式的 SQL where 子句以实现 CNF
【发布时间】:2015-11-30 18:06:18
【问题描述】:

我需要根据用户动态添加的属性(转置表)查询用户

我有 4 张桌子

  • 用户 - 为用户
  • propertyGroup - 对于属性组,可以将组动态添加到数据库中
  • propertyValue - 用于 propertyGroup 的可能值
  • usersPropertyValues - 将用户连接到他的相关属性

它看起来像这样:

users:
------
| id | name | details |
|----|------|---------|
|  1 |  Joe |     foo |
|  2 |  Dan |     bar |

propertyGroup:
--------------
| id |           name |
|----|----------------|
|  1 |        Hobbies |
|  2 |       LikeFood |
|  3 | VisitedCountry |

propertyValue:
--------------
| id | propertyGroupId |         name |
|----|-----------------|--------------|
|  1 |               1 | Technologies |
|  2 |               1 |      Surfing |
|  3 |               2 |         Rice |
|  4 |               2 |         Meat |
|  5 |               2 |          Veg |
|  6 |               3 |          USA |
|  7 |               3 |       FRANCE |
|  8 |               3 |       ISRAEL |
|  9 |               3 |       CANADA |

usersPropertyValues:
--------------------
| userId | propertyValueId |
|--------|-----------------|
|      1 |               1 |
|      1 |               2 |
|      1 |               3 |
|      1 |               5 |
|      1 |               6 |
|      1 |               7 |
|      1 |               8 |
|      2 |               2 |
|      2 |               3 |
|      2 |               4 |
|      2 |               5 |
|      2 |               8 |
|      2 |               9 |
|      2 |               7 |

所以一个 mix-em-all 查询将如下所示:

select * 
from users as u
 join usersPropertyValues as upv on upv.userId = u.id
 join propertyValues as pv on pv.id = upv.propertyValueId
 join propertyGroup as pg on pg.id = pv.propertyGroupId

| id | name | details | userId | propertyValueId | id | propertyGroupId |         name | id |           name |
|----|------|---------|--------|-----------------|----|-----------------|--------------|----|----------------|
|  1 |  Joe |     foo |      1 |               1 |  1 |               1 | Technologies |  1 |        Hobbies |
|  1 |  Joe |     foo |      1 |               2 |  2 |               1 |      Surfing |  1 |        Hobbies |
|  1 |  Joe |     foo |      1 |               3 |  3 |               2 |         Rice |  2 |       LikeFood |
|  1 |  Joe |     foo |      1 |               5 |  5 |               2 |          Veg |  2 |       LikeFood |
|  1 |  Joe |     foo |      1 |               6 |  6 |               3 |          USA |  3 | VisitedCountry |
|  1 |  Joe |     foo |      1 |               7 |  7 |               3 |       FRANCE |  3 | VisitedCountry |
|  1 |  Joe |     foo |      1 |               8 |  8 |               3 |       ISRAEL |  3 | VisitedCountry |
|  2 |  Dan |     bar |      2 |               2 |  2 |               1 |      Surfing |  1 |        Hobbies |
|  2 |  Dan |     bar |      2 |               3 |  3 |               2 |         Rice |  2 |       LikeFood |
|  2 |  Dan |     bar |      2 |               4 |  4 |               2 |         Meat |  2 |       LikeFood |
|  2 |  Dan |     bar |      2 |               5 |  5 |               2 |          Veg |  2 |       LikeFood |
|  2 |  Dan |     bar |      2 |               8 |  8 |               3 |       ISRAEL |  3 | VisitedCountry |
|  2 |  Dan |     bar |      2 |               9 |  9 |               3 |       CANADA |  3 | VisitedCountry |
|  2 |  Dan |     bar |      2 |               7 |  7 |               3 |       FRANCE |  3 | VisitedCountry |

全部在这里:http://sqlfiddle.com/#!3/49329/1

我想通过一组 propertyValueId 查询数据,并获取与该组属性匹配的所有用户,因此用户需要至少拥有每个属性组 - 换句话说,一个与一般 CNF 子句匹配的 where 子句,如下所示: (pvId是propertyValueId的缩写)

Property group A        Property group B              Property group C
(pvId_x or pvId_y) and (pvId_w or pvId_z) and... and (pvId_m or pvId_k) 

在上面的例子中pvId_x&pvId_y属于A组,pvId_w&pvId_z属于B组等等。

我没有做的事情, 我试图连接 IN 运算符的 AND 运算符(在模拟 or 部分 - 析取),如下所示(查询上述 sql fiddle):

select distinct u.name, u.id  
from users as u
 join usersPropertyValues as upv on upv.userId = u.id
 join propertyValues as pv on pv.id = upv.propertyValueId
 join propertyGroup as pg on pg.id = pv.propertyGroupId
 where (pv.propertyGroupId = 1 AND pv.id IN(1,2)) and (pv.propertyGroupId = 2 AND pv.id IN(5,6))

而不是同时获得两个用户(两者都有 (1 or 2) 和 (5 or 6) ) - 我没有。

我明白为什么结果集是空的,但我不明白如何实现正确的 where 子句。 - 怎么做?

我的问题: 上面的SQL结构中如何实现CNF逻辑?

编辑:异常结果示例:(关于 sqlfiddle 示例:

input  --> output 
{1,5}  --> Joe (user with hobby:tech, likefood:veg)
{2,8}  --> Joe,Dan (user with hobby:surfing, VisitedCountry:Israel)
{2,8,9}  --> Dan (user with hobby:surfing, VisitedCountry:Israel,Canada)

顺便说一句,我最终需要在 JPA 中实现这一点,所以如果 JPA 中有解决方案,那也很棒。但如果不是 - 我会翻译它...... 谢谢

【问题讨论】:

  • 你有所有这些漂亮的数据作为例子,然后你得到你的问题,你没有显示预期的结果......请包括那个
  • 感谢hogen - 将在几秒钟内完成
  • @Hogan - 添加示例
  • 例子很糟糕——幸运的是我明白你的意思
  • @Hogan 我只需要用户

标签: sql sql-server jpa where-clause conjunctive-normal-form


【解决方案1】:

据我了解,您希望查询与两个 propertyvalueid 关联的所有用户(因此 {1,5} 转到 hobby:tech、likefood:veg 的属性值 id)

一旦你说它很简单,首先获取一个列表,然后获取另一个列表,然后找到两者中的元素,如下所示:

select *
from users 
where id in (
  select userid from userPropertyValues where propertyvalueid  = 1
  intersect
  select userid from userPropertyValues where propertyvalueid  = 5
) sub

请注意,我可以使用内连接而不是相交运算符,但相交更性感。如果您的平台不支持它,请使用加入。


作为一个连接:

select *
from users 
join (select userid from userPropertyValues where propertyvalueid=1) a on a.userid = users.id
join (select userid from userPropertyValues where propertyvalueid=5) b on b.userid = users.id

或更简单地表述为(这是 AND 条件 - 1 和 5)

select *
from users 
join userPropertyValues a on a.userid = users.id and a.propertyvalueid=1
join userPropertyValues b on b.userid = users.id and b.propertyvalueid=5

(这是 OR 条件 - 1 OR 5)

select *
from users 
left join userPropertyValues a on a.userid = users.id and a.propertyvalueid=1
left join userPropertyValues b on b.userid = users.id and b.propertyvalueid=5
where coalesce(a.userid, b.userid) is not null

你也可以说

where a.userid is not null or b.userid is not null

---

小提琴

select *
from users 
where id in (
  select userid from usersPropertyValues where propertyvalueid  = 1
  intersect 
  (
  select userid from usersPropertyValues where propertyvalueid  = 3 
    UNION ALL
  select userid from usersPropertyValues where propertyvalueid  = 4
  )
);

让我们看到相交之前的部分

select *
from users 
join userPropertyValues a on a.userid = users.id and a.propertyvalueid=1

后面的部分是

select *
from users 
left join userPropertyValues b on b.userid = users.id and b.propertyvalueid=1
left join userPropertyValues c on c.userid = users.id and c.propertyvalueid=5
where coalesce(b.userid, c.userid) is not null

所以把它们放在一起(对于相交来说两者都是正确的,因为它与 AND 相同):

select *
from users 
join userPropertyValues a on a.userid = users.id and a.propertyvalueid=1
left join userPropertyValues b on b.userid = users.id and b.propertyvalueid=1
left join userPropertyValues c on c.userid = users.id and c.propertyvalueid=5
where coalesce(b.userid, c.userid) is not null

【讨论】:

  • 这看起来像解决方案,我在每个属性组之间添加了 union all 即:“select * from users where id in ( (select.. union all select .....) intersect ( select.... union all select ..... ) intersect ...(select.... union all select ..... )" 以匹配 CNF 结构(您的解决方案不完整)- 'JOIN' 怎么做?
  • @yossico - 我不理解您的联合所有评论,这听起来像是一个错误,但我必须查看您的查询全文才能确定。加入我的答案中添加的示例。
  • 您可以在此处查看 union all 部分的详细示例:sqlfiddle.com/#!3/8d830/1。主要问题是,在相同类别的所有值之间需要 OR 条件以及在组之间 - AND。是否可以通过 Joins 来实现?
  • 对,intersect 是一个“AND”,union all 是一个“OR”
  • @yossico - 好的,我做到了,但这是我要为你做的最后一个马戏团戏法。如果您有其他“问题”,请将它们作为新问题提出。你在这里有点辱骂我的好意。一旦您理解了我的答案,最后一个附加问题就非常简单,而且您可以自己完成我敢肯定。如果它回答了您的问题,请将我的回答标记为正确 - 我相信它确实如此。
【解决方案2】:

我认为当你在 where 子句中更改时会有所帮助

其中 (pv.propertyGroupId = 1 AND pv.id IN(1,2)) (pv.propertyGroupId = 2 AND pv.id IN(5, 6))

【讨论】:

  • 但在这种情况下,我会得到只有“5”且没有 1 或 2 的用户
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多