SQL 3 个表的初学者查询逻辑

SQL Beginner Query Logic With 3 Tables

sql 表如下:likes 列 drinker 和 beer,sells 列 bar 和 beer,frequent 列 drinker 和 bar。

我之前已经回答过以下陈述:

"Drinkers who frequent bars which serve some beer they like"

与 SQL 查询:

SELECT DISTINCT y.drinker
FROM likes a, sells, frequents y
WHERE a.beer = sells.beer
AND sells.bar = y.bar
AND y.drinker = a.drinker;

现在,我正在尝试修改上面列出的查询以回答类似但不同的陈述:

"Drinkers who only frequent bars which serve beers they like"

从逻辑上讲,修改是只包括那些经常光顾提供他们喜欢的啤酒的酒吧的人,并排除那些光顾任何不提供他们喜欢的啤酒的酒吧的人。

我最难修改上面的查询以满足第二个语句。

我的想法可能是做一个双重否定,例如:获取以下组中不存在的饮酒者列表:不经常光顾不出售他们喜欢的啤酒的酒吧的饮酒者。但是正确的实现让我不知所措。

这是我解决此查询的最佳尝试,但我很清楚这是不正确的,因为此查询仍然 returns 经常光顾(不仅)提供他们喜欢的啤酒的酒吧的饮酒者:

SELECT distinct x.drinker
FROM frequents x
WHEREexists (SELECT* 
             FROM frequents y, sells z, likes u
             WHERE x.drinker=y.drinker
             AND y.bar = z.bar and z.beer = u.beer
             AND y.drinker = u.drinker);

任何帮助都会很棒,感谢您提供的任何见解。

我为你构建了一些 fiddle 并提出了你已经建议的内容:一个基于消极 select 饮酒者的查询,这些饮酒者去的地方他们的啤酒不是服务:

SELECT DISTINCT fdrinker FROM frequents f WHERE NOT EXISTS (
 SELECT 1 FROM frequents WHERE NOT EXISTS
 (SELECT 1 FROM sells WHERE sbar=fbar AND
  sbeer IN (SELECT lbeer FROM likes WHERE ldrinker=fdrinker) )
 AND fdrinker=f.fdrinker )

最里面的子select列出了特定饮酒者的所有"liked"饮料。如果 至少销售一种 喜欢的啤酒,则下一个外部子 select 级别将从 sells table 中列出此酒吧。下一层然后检查经常光顾的酒吧是否存在 "fail to deliver" 的任何酒吧。如果没有这样的 "failure" 发生,顶级 select 将向饮酒者展示他们只会经常光顾至少提供一种他们喜欢的啤酒的酒吧。

我认为这是一个有效的解决方案...

子查询用于过滤掉经常光顾他们喜欢的啤酒数量为 0 的酒吧的饮酒者。

select distinct drinker 
from frequents 
where drinker not in (
    select f.drinker
    from frequents f 
    join sells s on f.bar = s.bar
    left join likes l on l.drinker = f.drinker and l.beer = s.beer
    group by f.drinker, f.bar
    having count(l.drinker) = 0
);

Sample SQL Fiddle

经常去的酒吧数量与他们经常去的酒吧数量完全相同的饮酒者:

select drinker
from frequents
group by drinker
having count(bar) = (
    select count(distinct f.bar)
    from
        sells s
        inner join likes l on l.beer = s.beer
        inner join frequents f on f.bar = s.bar and f.drinker = l.drinker
    where f.drinker = frequents.drinker
)

当你无法匹配所有三个关系三角形时,内部计数会降低。 (请注意 beer、bar 和 drink 的每个测试如何在内部查询中各出现一次。)如果您想查找相反的一组人,只需将等式测试更改为大于即可。

编辑:

我正在考虑解决这个问题的替代方法。有趣的是,这是您可能会发现右外连接有用的地方(而不是连接 tables 周围的括号。)

frequents table 仍然是下面查询的重点,逻辑是由找出哪些经常光顾的酒吧也是 like/favorite 可用的酒吧的想法驱动的待售。由于外部连接,"frequents" 端永远不会为空,但 "likes-sells" 端只有在无法匹配该频繁的一对时才会有空值。 having 子句中的最终测试是多余的,但我想展示能够匹配“frequents”两列计数的对称性,即使只有一个是绝对必要的。

select f.drinker
from
    frequents f left outer join (
        likes l inner join sells s on s.beer = l.beer
    ) on f.drinker = l.drinker and f.bar = s.bar
group by f.drinker
having count(f.drinker) = count(l.drinker) and count(f.bar) = count(s.bar)
w=12=WILL.y.:w=11=w w=10=sh