来自同一个表的SQL求和值

时间:2013-09-19 21:57:43

标签: sql oracle

我有一张桌子爱好,其片段如下:

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;

然而,这给了我一个错误的答案。我的代码出了什么问题?

4 个答案:

答案 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