使用SQL作为行统计信息的年龄间隔

时间:2012-10-31 08:38:46

标签: sql

我正在尝试创建此摘要统计信息表,计算每个类别中的个人数量

Agegroup   |    All    |    Female   |    Male
------------------------------------------------
All        |  560594   |    34324    |   234244
< 20       |   4324    |     545     |    3456
20 - 30    |  76766    |     3424    |   32428
30 - 40    |  36766    |     764     |   82427
40 - 50    |  46766    |     4324    |   72422
50 - 60    |  66766    |     3424    |   52424
> 60       |  76766    |    43424    |   12423

来自此表

PersonID  |   Age   |  Sex   
----------------------------
  A      |    43    |   F
  B      |    22    |   F
  C      |    65    |   M
  D      |    33    |   F
  E      |    28    |   M

这甚至可以“一次性”使用SQL吗?我试验过这个,但它并没有真正融合在一起......

SELECT SUM(CASE WHEN Age < 20 THEN 1 ELSE 0 END) AS [Under 20],
    SUM(CASE WHEN Age BETWEEN 20 AND 30 THEN 1 ELSE 0 END) AS [20-30],
    SUM(CASE WHEN Age BETWEEN 30 AND 40 THEN 1 ELSE 0 END) AS [30-40]
FROM Persons

5 个答案:

答案 0 :(得分:4)

我相信以下是实现这一目标的最简单方法,即使在该年龄段内没有人,也可以获得该行。此外,由于Sex只有2个可能的值,因此您可以使用NULLIF代替案例表达式。

SELECT  [Agegroup] = Name,
        [All] = COUNT(Person.PersonID),
        [Female] = COUNT(NULLIF(Person.Sex, 'M')),
        [Male] = COUNT(NULLIF(Person.Sex, 'F'))
FROM    (VALUES 
            (0, 1000, 'All'),
            (0, 20, '< 20'),
            (20, 30, '20 - 30'),
            (30, 40, '30 - 40'),
            (40, 50, '40 - 40'),
            (50, 60, '50 - 40'),
            (60, 1000, '> 60')
        ) AgeRange (MinValue, MaxValue, Name)
        LEFT JOIN Person
            ON Person.Age >= AgeRange.MinValue
            AND Person.Age < AgeRange.Maxvalue
GROUP BY AgeRange.Name, AgeRange.MinValue, AgeRange.Maxvalue
ORDER BY AgeRange.MinValue, AgeRange.MaxValue DESC

<强> Example on SQL Fiddle

答案 1 :(得分:2)

也许是这样的:

DECLARE @T TABLE(PersonID VARCHAR(5), Age INT,Sex VARCHAR(5))

INSERT INTO @T
VALUES
    ('A',43,'F'),
    ('B',22 ,'F'),
    ('C ',65,'M'),
    ('D',33,'F'),
    ('E',28,'M')

SQL

SELECT
    'All' AS Agegroup,
    COUNT(*) AS [All],
    SUM(CASE WHEN tbl.Sex='F' THEN 1 ELSE 0 END) AS Female,
    SUM(CASE WHEN tbl.Sex='M' THEN 1 ELSE 0 END) AS Male
FROM
    @T AS tbl
UNION ALL   
SELECT
    tbl.Agegroup,
    COUNT(*) AS [All],
    SUM(CASE WHEN tbl.Sex='F' THEN 1 ELSE 0 END) AS Female,
    SUM(CASE WHEN tbl.Sex='M' THEN 1 ELSE 0 END) AS Male
FROM
(
    SELECT
        (
            CASE 
            WHEN Age BETWEEN 0 and 20
            THEN '< 20'
            WHEN Age BETWEEN 20 and 30
            THEN '20 - 30'
            WHEN Age BETWEEN 30 and 40
            THEN '30 - 40'
            WHEN Age BETWEEN 40 and 50
            THEN '40 - 50'
            WHEN Age BETWEEN 50 and 60
            THEN '50 - 60'
            WHEN Age> 60
            THEN '> 60'
            END
        ) AS Agegroup,
        t.Age,
        t.Sex
    FROM
        @T AS t
) AS tbl
GROUP BY
    tbl.Agegroup

答案 2 :(得分:2)

您最好的模式是创建一个年龄范围表(或下面示例中的虚拟表)并加入它,然后转动结果以将结果转换为柱状形式。

select range as AgeGroup, m as Male, F as Female, m+f as [all]
from
(  
    select PersonID, range, sex  
    from
    (
        select 'all' as range, 0 as minval, 200 as maxval
        union select '<20',0,19
        union select '20-29',20,29
        -- etc....
    ) ranges
        left join
    yourtable t
        on t.age between minval and maxval
) src
pivot
    (count(personid) for sex in ([m],[f])) p

答案 3 :(得分:1)

试试这个:

;with Age_range as(
select '<20' as age union all
select '20 - 30' as age union all
select '30 - 40' as age union all
select '40 - 50' as age union all
select '50 - 60' as age union all
select '>60' as age  
),
cte as(
select [Sex],
sum(case when [Age]<20 then 1 else 0 end) as '<20' ,
sum(case when [Age]between 20 and 29 then 1 else 0 end) as '20 - 30',
sum(case when [Age]between 30 and 39 then 1 else 0 end) as '30 - 40',
sum(case when [Age]between 40 and 49 then 1 else 0 end) as '40 - 50',
sum(case when [Age]between 50 and 59 then 1 else 0 end) as '50 - 60',
sum(case when [Age]>=60 then 1 else 0 end) as '>60' 
 from Persons
 group by [Sex]),
 cte1 as(select Sex,'<20'  as cnt from cte where [<20]>0 union all
 select Sex,'20 - 30'  as cnt from cte where [20 - 30]>0 union all
 select Sex,'30 - 40'  as cnt from cte where [30 - 40]>0 union all
 select Sex,'40 - 50'  as cnt from cte where [40 - 50]>0 union all
 select Sex,'50 - 60'  as cnt from cte where [50 - 60]>0 union all
 select Sex,'>60'  as cnt from cte where [>60]>0)
 select A.age,
        COUNT(case when sex in ('M','F') then 1 end) as [All],
        COUNT(case when sex='F' then 1 end) as Female,
        COUNT(case when sex='M' then 1 end) as Male
  from Age_range A left join cte1 C
  on A.age=C.cnt
  group by A.age


SQL Fiddle Demo

答案 4 :(得分:0)

select  'All' as [Age Group]
.       count(*) as [All],
,       sum(case Sex when 'F' then 1 end) as Female
,       sum(case Sex when 'M' then 1 end) as Male
from    Persons
union all
select  '< 20' as [Age Group]
.       count(*) as [All],
,       sum(case Sex when 'F' then 1 end) as Female
,       sum(case Sex when 'M' then 1 end) as Male
from    Persons
where   Age < 20
union all
select  '20 - 30' as [Age Group]
.       count(*) as [All],
,       sum(case Sex when 'F' then 1 end) as Female
,       sum(case Sex when 'M' then 1 end) as Male
from    Persons
where   20 <= Age and Age < 30
union all
...