我有一张桌子爱好,其片段如下:
Name Activity Hours
John Hiking .5
Sam Cycling .5
Sam Swimming 1
Sam Hiking .5
John Running 1
Sam Sailing 1
对于(X,Y)中的每个人X,我想找到X和Y没有共同点的活动小时数。例如,如果John = X且Sam = Y,那么它将产生1,因为Running是John没有的唯一活动。
我的代码如下:
select a.Name, b.Name, sum(a.Hours)
from Hobby a, Hobby b
where a.Name <> b.Name and a.Activity <> b.Activity
group by a.Name, b.Name;
然而,这给了我一个错误的答案。我的代码出了什么问题?
答案 0 :(得分:4)
我发现这是一个棘手的问题。我最初的方法是使用full outer join
。但后来我意识到如果一个名字中的活动没有匹配,那么我也不会有这个名字。
因此,以下查询通过获取所有名称对的列表来工作。这是一个有序列表,因此一对给定的名称只出现一次。然后将其与Hobby
表连接两次,使用left outer join
来获取匹配项。但关键是,当没有匹配时,其上带有Activity
的行仍然存在,但值为NULL
。
where
子句查找在任一表中都有Activity
的所有NULL
。这些是不匹配的。然后,只需将时间加起来就可以了:
select names.Name1, names.Name2, sum(coalesce(h1.hours, h2.hours))
from (select distinct h1.Name as name1, h2.Name as name2
from Hobby h1 cross join Hobby h2
where h1.Name < h2.Name
) names left outer join
Hobby h1
on names.name1 = h1.name left outer join
Hobby h2
on names.name2 = h2.name and
h1.Activity = h2.Activity
where h1.Activity is null or h2.Activity is null
group by names.Name1, names.Name2;
答案 1 :(得分:0)
你的from子句读取
FROM Hobby a, Hobby b
在from子句中加上逗号意味着“CROSS JOIN”,这意味着第一个表中的每一行都与第二个表中的每一行相关联。鉴于你的where子句,我认为这会给出一些相当大的数字。
您的查询需要有所不同:
select sum(hours)
from hobby
where name = 'John'
and activity not in (
select activity
from hobby
where name = 'Sam'
)
答案 2 :(得分:0)
如果您获取人员和活动列表,并加入所有其他人的列表,并测试其他人是否也参与该活动,那么应该这样做。
with
cte_unique_names as (
select distinct name
from hobby)
select
h.name participates,
n.name does_not_participate,
sum(hours) hours
from
hobby h
cross join
cte_unique_names n
where
n.name != h.name and
not exists (
select null
from hobby h2
where h2.name = n.name and
h2.activity = h.activity)
group by
h.name,
n.name
答案 3 :(得分:0)
在组之前考虑基础结果。仅考虑表a为John, Hiking
的行。在您的查询中,您将拥有
John Hiking 0.5 John Hiking 0.5
John Hiking 0.5 Sam Cycling 0.5
John Hiking 0.5 Sam Swimming 1
John Hiking 0.5 Sam Hiking 0.5
John Hiking 0.5 John Running 1
John Hiking 0.5 Sam Sailing 1
使用where子句,您将从表b中删除John行和Hiking行,并离开:
John Hiking 0.5 Sam Cycling 0.5
John Hiking 0.5 Sam Swimming 1
John Hiking 0.5 Sam Sailing 1
所以你要在John, Sam
集合中计算这三个小时。
这是一种调整它的方法,同时仍然只使用每个表一次:
Select
a.Name,
b.Name Name2,
Sum(a.Hours) / count(distinct b.activity)
- Sum(case when a.Activity = b.Activity then a.Hours else 0 end) as Hours
From
Hobby a,
Hobby b
Where
a.Name != b.Name
Group By
a.Name,
b.Name
<强> Example Fiddle 强>