在sql server中基于年龄组的COUNT后得到最高数字

时间:2017-03-20 14:43:41

标签: sql sql-server

我有两张桌子:技术人员和成员。会员只能拥有一个技术(他/她最喜欢的技术项目)。

techs: techid,tname
members: memberid,age,techcode

示例数据现在(对于技术人员):

techid    tname
  1        Mobile
  2        XBox

成员:

memberid   age   techcode
  1         8     1
  2         18    1
  3         11    2
  4         42    1

它继续......

现在,感兴趣的第一个报告/查询是获得每个年龄组的每个技术项目的总受欢迎程度。为此,以下查询做得很好。

select tname, '0-10' = count(case when age<=10 then 1 end)
,'11-20' = count(case when age BETWEEN 11 AND 20 then 1 end)
,'21-30' = count(case when age BETWEEN 21 AND 30 then 1 end)
,'31-40' = count(case when age BETWEEN 31 AND 40 then 1 end)
,'41-50' = count(case when age BETWEEN 41 AND 50 then 1 end)
,'51-60' = count(case when age BETWEEN 51 AND 60 then 1 end)
,'61-70' = count(case when age BETWEEN 61 AND 70 then 1 end)
,'71-80' = count(case when age BETWEEN 71 AND 80 then 1 end)
,'81-90' = count(case when age BETWEEN 81 AND 90 then 1 end)
,'91-100' = count(case when age BETWEEN 91 AND 100 then 1 end)
,'100+' = count(case when age >100 then 1 end)
from members,techs WHERE techcode=techid GROUP BY tname

我想要的下一份报告现在可以获得每个年龄段最受欢迎的项目。例如,当前的输出是:

tname    0-10  11-20

Mobile   7     1
XBox     4     20

我想要的下一个输出基本上是移动最受欢迎的0-10,XBox很受欢迎,适合11-20岁。我现在想要的输出大致是:

Age    Highest     Tech

0-10   7         Mobile
11-20  20        Xbox

我现在真的没有查询。我试图在每个年龄范围内做MAX(),但是因为聚合不能在聚合上运行而失败了。我对如何绕过它感到有点迷失,因此没有真正的查询尝试显示。

3 个答案:

答案 0 :(得分:0)

您需要同时按两个维聚合(按行,而不是列)。然后使用row_number()获取最常用的值:

select ta.*
from (select tname, agegrp, count(*) as cnt,
             row_number() over (partition by agegrp order by count(*) desc) as seqnum
      from members m join
           techs t 
           on m.techcode = t.techid cross join
           (values (case when age <= 10 then  '0-10'
                         when age <= 20 then '11-20'
                         . . .
                    end)
           ) v(agegrp)
      group by tname, agegrp
     ) ta
  where seqnum = 1;

注意:

  • 从不FROM子句中使用逗号。 始终使用正确的JOIN语法。
  • cross apply提供了一种为表达式指定别名的便捷方法。
  • 您想要获得的价值称为mode

答案 1 :(得分:0)

这是另一种方法,使用common table expression row_number() ,但也使用common table expressiontable value constructor代替case表达。

;with AgeGroup as (
  select *
  from (values 
      (0 ,10 ,'0-10')
    , (11,20,'11-20')
    , (21,30,'21-30')
    , (31,40,'31-40')
    , (41,50,'41-50')
    , (51,60,'51-60')
    , (61,70,'61-70')
    , (71,80,'71-80')
    , (81,90,'81-90')
    , (91,100,'91-100')
    , (100,null,'100+')
      ) as t(ageMin,ageMax,AgeGroup)
)
, cte as (
select
    AgeGroup
  , Highest = count(*)
  , Tech = tname
  , rn = row_number() over (
      partition by ageGroup
      order by count(*) desc
      )
from AgeGroup ag
  inner join members m 
    on m.age >= ag.ageMin
   and m.age <= ag.ageMax
  inner join techs t
    on m.techcode = t.techid
group by AgeGroup, tname
)
select ageGroup, Highest, Tech
from cte
where rn = 1

rextester演示:http://rextester.com/KMASZX6347

如果您想显示没有数据的年龄段,可以将inner join更改为left join

答案 2 :(得分:0)

使用RANK() OVERUNION可以获得您所追求的结果:

SELECT TOP 1 Age, Total, RANK() OVER (ORDER BY Total DESC) AS RankByTotal, Tech
        FROM(


        SELECT '0-10' Age,
        sum(A.[0-10]) Total
        ,Tech
         FROM(
                SELECT tname, Tech
                ,'0-10'  = COUNT(case when age  between 0 and 10 then 1 end) 
                FROM members m
                LEFT JOIN techs t on techcode= techid
                Group by tname, Tech) A
         Group by Tech ) B

UNION ALL 

SELECT TOP 1 Age, Total,    RANK() OVER (ORDER BY Total DESC) AS RankByTotal, Tech
        FROM(

            SELECT '11-20' Age,
            sum(A.[11-20]) Total
            ,Tech
             FROM(
                SELECT tname, Tech 
                ,'11-20' = COUNT(case when age between 11 and 20 then 1 end)
                FROM members m
                LEFT JOIN techs t on techcode= techid
                Group by tname, Tech) A
         Group by Tech) B

这种方法对每个年龄组来说意味着UNION,这可能不是一种理想的方法。 但是,它的工作原理!