简体   繁体   English

SQL查询以查找多个条件的匹配项

[英]SQL query to find matches for multiple criteria

If I had a PERMISSIONS table that looked like this: 如果我有一个PERMISSIONS表,看起来像这样:

PERSON         PERMISSION
------         ----------
Bob            red
John           red
John           blue
Mary           red
Mary           blue
Mary           yellow

and a THINGS table that looks like this: 和一个看起来像这样的THINGS表:

THING          PERMISSION
-----          ----------
apple          red
eggplant       red
eggplant       blue

I'm trying to come up with a pure SQL query that would let me find out what PERSON s have access to what THING s. 我试图想出一个纯粹的SQL查询,将让我找出什么PERSON ■找访问哪些THING秒。 Basically, I want a query that would look something like: 基本上,我想要一个看起来像这样的查询:

SELECT person
  FROM ... vague handwaving here ...
 WHERE thing = 'eggplant'

and have it return "John" and "Mary". 并让它返回“约翰”和“玛丽”。 The key point being the number of permissions necessary for access to the thing is arbitrary. 关键点是访问该事物所需的权限数量是任意的。

I feel like this should be obvious, but I just can't come up with an elegant solution. 我觉得这应该是显而易见的,但我无法想出一个优雅的解决方案。 Oracle compatible solutions preferred. Oracle兼容解决方案首选。

Edit: 编辑:

Solutions from Kosta and JBrooks work well. Kosta和JBrooks的解决方案运作良好。 Below is a modified version of Kosta's solution that only hits the indexes twice, as opposed to 3x for Kosta's and 4x for JBrooks's (though I agree with JBrooks that this is probably unnecessary optimization). 下面是Kosta解决方案的修改版本,它只能击中索引两次,而不是Kosta的3倍和JBrooks的4倍(尽管我同意JBrooks认为这可能是不必要的优化)。

SELECT p.person, num_permission, COUNT(p.person)
FROM permissions p
INNER JOIN (
    SELECT permission,
           COUNT(1) OVER (PARTITION BY thing) AS num_permission
      FROM things
     WHERE thing = 'eggplant'
  ) t ON t.permission = p.permission 
GROUP BY p.person, num_permission
HAVING COUNT(p.person) = num_permission

Ok, I get why people might use the Count() to match on and that could work, but I think that you will get into trouble doing this when things get a little more complicated (and they always get a little more complicated.) 好吧,我明白为什么人们可能会使用Count()进行匹配,这可能会有效,但我认为当事情变得复杂一点时,你会遇到麻烦(而且它们总是变得复杂一点。)

If I say this problem in English it is: 如果我用英语说这个问题,那就是:

  1. Select the PEOPLE that have permission on that THING. 选择拥有该THING权限的PEOPLE。
  2. and there doesn't exist a PERMISSION required for that THING that the PERSON doesn't have. 并且不存在PERSON所没有的那种许可所允许的许可。

So that SQL would be: 所以SQL将是:

SELECT DISTINCT P.PERSON, T.THING
FROM PERMISSIONS P
INNER JOIN THINGS T
ON P.PERMISSION = T.PERMISSION
WHERE NOT EXISTS
    (SELECT 1
    FROM THINGS TSUB
    WHERE TSUB.THING = T.THING
    AND TSUB.PERMISSION NOT IN
        (SELECT PSUB.PERMISSION
        FROM PERMISSIONS PSUB
        WHERE PSUB.PERSON = P.PERSON))
ORDER BY P.PERSON, T.THING

I'm a SQL Server guy now so the syntax might be a little off, but you get the idea. 我现在是一个SQL Server人,所以语法可能有点偏,但你明白了。

Performance: Someone is going to say that this doesn't look like the most efficient SQL, so let me defend myself now. 性能:有人会说这看起来不是最有效的SQL,所以让我现在为自己辩护。 Permission and user tables are usually on the smaller side compared to the rest of a system and with DBMS today you would be hard pressed to load enough data in these tables to make this statement run longer than a tenth of a second - especially after indexes were in use. 与系统的其余部分相比,权限和用户表通常较小,而今天使用DBMS,您很难在这些表中加载足够的数据,以使此语句的运行时间超过十分之一秒 - 尤其是在索引为正在使用。 So in the past I would have agree about the performance - today I never worry about the performance unless it jumps out at me. 所以在过去我会同意这个表现 - 今天我从不担心表演,除非它跳出来对我说。 So much less maintenance when you approach it this way. 当你以这种方式接近它时,更少的维护。

Use: 使用:

  SELECT p.person
    FROM PERMISSIONS p
    JOIN THINGS t ON t.permission = p.permission
   WHERE t.permission IN ('red', 'blue')
GROUP BY p.person
  HAVING COUNT(DISTINCT t.permission) = 2

The WHERE clause ensures that only values red and blue are included. WHERE子句确保仅包含红色和蓝色值。 The COUNT DISTINCT ensures that duplicates (two blue's) are not allowed, as that would be a false positive. COUNT DISTINCT确保不允许重复(两个蓝色),因为这将是误报。

select person
from permissions 
where permission in (select permission from things where thing='eggplant')
group by person
having count(person) = (select count(permission)  from things where thing='eggplant')
select distinct person
from permissions p
join things      t on t.permission = p.permission
                  and t.thing      = 'eggplant'

ought to do it. 应该这样做。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM