如果我有一个PERMISSIONS
表,看起来像这样:
PERSON PERMISSION
------ ----------
Bob red
John red
John blue
Mary red
Mary blue
Mary yellow
和一个看起来像这样的THINGS表:
THING PERMISSION
----- ----------
apple red
eggplant red
eggplant blue
我正在尝试提出一个纯SQL查询,让我找出PERSON
可以访问THING
的内容。基本上,我想要一个看起来像这样的查询:
SELECT person
FROM ... vague handwaving here ...
WHERE thing = 'eggplant'
让它返回“John”和“Mary”。关键点是访问该事物所需的权限数量是任意的。
我觉得这应该是显而易见的,但我无法想出一个优雅的解决方案。首选Oracle兼容解决方案。
编辑:
Kosta和JBrooks的解决方案运作良好。下面是Kosta解决方案的修改版本,只有两次攻击指数,而Kosta's的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
答案 0 :(得分:2)
好的,我明白为什么人们可能会使用Count()来匹配,这可能会有效,但我认为当事情变得复杂时你会遇到麻烦(而且它们总是变得更复杂一些。)
如果我用英语说这个问题,那就是:
这样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
我现在是一个SQL Server人员,所以语法可能有些偏差,但你明白了。
性能:有人会说这看起来不是最有效的SQL,所以让我现在为自己辩护。与系统的其余部分相比,权限和用户表通常较小,而今天使用DBMS,您很难在这些表中加载足够的数据,以使此语句的运行时间超过十分之一秒 - 尤其是在索引为正在使用。所以在过去我会同意这个表现 - 今天我从不担心表演,除非它跳出来对我说。当你接近这种方式时,维护要少得多。
答案 1 :(得分:1)
使用:
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
WHERE子句确保仅包含红色和蓝色值。 COUNT DISTINCT
确保不允许重复(两个蓝色),因为这将是误报。
答案 2 :(得分:1)
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')
答案 3 :(得分:0)
select distinct person
from permissions p
join things t on t.permission = p.permission
and t.thing = 'eggplant'
应该这样做。