【问题标题】:Count male, female and total计算男性,女性和总数
【发布时间】:2013-06-11 17:34:38
【问题描述】:

我想计算指定年份学生表中的男性、女性和学生总数。我希望结果能以如下形式显示:

====================================
| Label    |  Value   |   Year     |
====================================
| Male     |   0      |   2013     |
| Female   |  23      |   2013     |
| Total    |  23      |   2013     |
====================================

如果指定年份没有男性/女性匹配,则查询应显示 0。知道如何实现这一点吗?

提前致谢

【问题讨论】:

  • 也许您可以提供 DDL 和您当前的查询?
  • 学生表(id、姓名、性别、注册年份)
  • 这么多不同的选项供您选择:)您不只是喜欢 SQL!
  • 感谢您收拾我的星期五晚上:p

标签: sql sql-server


【解决方案1】:

考虑以下查询:

select
  max(registeredYear) as year,
  count(case when gender='Male' then 1 end) as male_cnt,
  count(case when gender='Female' then 1 end) as female_cnt,
  count(*) as total_cnt
from student
where registeredYear = 2013
group by registeredYear;

结果会是这样的:

Year male_cnt female_cnt total_cnt
---- -------- ---------- ---------
2013        0         23        23

您可以将此结果转换为您想要的形式。如果你想在查询中这样做,那么你可以这样做:

with t as (
    select
      max(registeredYear) as year,
      count(case when gender='Male' then 1 end) as male_cnt,
      count(case when gender='Female' then 1 end) as female_cnt,
      count(*) as total_cnt
    from student
    where registeredYear = 2013
    group by registeredYear)
select 'Male', male_cnt as male, year from t
union all
select 'Female', female_cnt as male, year from t
union all
select 'Total', total_cnt as male, year from t
;

【讨论】:

  • 感谢您的回复。但是,当我将年份更改为没有数据的 2014 年时,它不会返回任何内容。我期待 2014|0|0|0
  • @aby count() 函数将始终返回数字。如果没有满足 where 条件的数据,则返回0。但是年份部分将是空的...
  • @aby 也许您可以使用coalesce() 函数和绑定变量,如max(coalesce(registeredYear, :inputYear))... 其中:input_year 是您应该指定的绑定变量。我不确定 SQL Server 如何使用绑定变量,但你会知道的。
【解决方案2】:

只需运行此查询...

SELECT 
     MAX(registeredYear) as Year
    ,SUM(CASE WHEN gender = 'Male' THEN 1 END) AS Male
    ,SUM(CASE WHEN gender = 'Female' THEN 1 END) AS Female
    ,SUM(CASE WHEN gender IS NOT NULL THEN 1 ELSE 0 END) AS Total
 FROM from student
 WHERE registeredYear = 2013
 GROUP BY registeredYear;

【讨论】:

    【解决方案3】:

    类似这样的:

    select 'Male' as Label, count(gender) as Value from student where gender= 'Male'
    union (
    select 'Female' as Label, count(gender) as Value from student where gender= 'Female' )
    union (
    select 'Total' as Label, count(gender) as Value from student )
    

    【讨论】:

    • 这个查询会读表3次,效率低。
    • 只适用于小桌子的好解决方案
    【解决方案4】:

    试试这个,假设 Gender 或 RegisteredYear 中没有空值:

    WITH AllYears AS
    (
       SELECT RegisteredYear
       FROM Student
       GROUP BY RegisteredYear
    )
    
    , AllGenders AS
    (
       SELECT Gender
       FROM Student
       GROUP BY Gender
    )    
    
    , AllGendersAndYears AS
    (
       SELECT Gender, RegisteredYear
       FROM AllGenders, AllYears
    )
    
    SELECT Gender, RegisteredYear, CountForGenderAndYear
    FROM AllGendersAndYears 
       CROSS APPLY 
       (
          SELECT COUNT(*) AS CountForGenderAndYear
          FROM Student
          WHERE Student.Gender = AllGendersAndYears.Gender
             AND Student.RegisteredYear = AllGendersAndYears.RegisteredYear
       ) countForGenderAndYear
    
    UNION ALL
    
    SELECT 'Total', AllYears.RegisteredYear, CountForYear
    FROM AllYears
       CROSS APPLY 
       (
          SELECT COUNT(*) AS CountForYear
          FROM Student
          WHERE Student.RegisteredYear = AllYears.RegisteredYear
       ) countForYear
    

    【讨论】:

    • 哦,这会在表格中获得每年的摘要。你想要一个特定的年份。为此,请将 AllYears 替换为 AllYears AS (SELECT 2014 AS RegisteredYear)
    • 谢谢,您的解决方案非常接近。但是,当没有要返回的所选年份的数据时,我应该如何更改以便返回 Female|0|2014, Male|0|2014, Total|0|2014?
    • 啊,我之前在性别/年份计数方面犯了同样的错误(如果没有,它返回“1”)但我现在已经以同样的方式解决了。
    【解决方案5】:

    这是另一个变体,使用 UNPIVOT。这个专门搜索男性和女性,所以它不像我的另一个那样灵活(因为你需要对每个性别进行硬编码)。但它可能是最有效的。

    WITH AllYears (RegisteredYear) AS
    (
        --SELECT DISTINCT RegisteredYear
        --FROM Student
    
         --...OR...
    
        SELECT 2014
    )
    , GenderAndYearCounts AS
    (
        SELECT RegisteredYear
            , SUM(CASE Gender WHEN 'MALE' THEN 1 ELSE 0 END) MaleCount
            , SUM(CASE Gender WHEN 'FEMALE' THEN 1 ELSE 0 END) FemaleCount
            , COUNT(*) YearCount
        FROM Student
        GROUP BY RegisteredYear
    )
    , GenderAndYearCountsForAllYears AS
    (
        SELECT AllYears.RegisteredYear
            , ISNULL(MaleCount, 0) AS MaleCount
            , ISNULL(FemaleCount, 0) AS FemaleCount
            , ISNULL(YearCount, 0) AS YearCount
        FROM AllYears
            LEFT JOIN GenderAndYearCounts ON GenderAndYearCounts.RegisteredYear = AllYears.RegisteredYear
    )
    
    SELECT Label, Value, RegisteredYear
    FROM 
    (
        SELECT RegisteredYear, MaleCount AS Male, FemaleCount AS Female, YearCount AS Total
        FROM GenderAndYearCountsForAllYears
    ) allCounts
    
    UNPIVOT
    (
        Value FOR Label IN (Male, Female, Total)
    ) unpivotted
    

    【讨论】:

      【解决方案6】:

      所有性别,然后是所有年份,然后是计数:

      declare @Year int
      set @Year = 2014
      
      select labels.label,
          counts.cnt,
          @Year as registeredYear
          from 
              (select 'Male' as label, 1 as sortOrder
              union all 
              select 'Female', 2
              union all
              select 'All', 3) as labels 
         left join
             (select gender, 
              count(1) cnt
              from student
              where registeredYear = @Year
              group by gender) as counts
          on labels.label = counts.gender     
          order by labels.sortOrder
      

      【讨论】:

        【解决方案7】:

        自从你shouldn't mix grid formatting with data retrieval

        SELECT
          SUM(CASE WHEN gender = 'Male' THEN 1 ELSE 0 END) as MaleCount,
          SUM(CASE WHEN gender = 'Female' THEN 1 ELSE 0 END) as FemaleCount,
          COUNT(*) as TotalCount
        FROM student
        WHERE registeredYear = 2013
        

        【讨论】:

          【解决方案8】:

          我相信这与只需通过学生表一次即可获得的效率一样高。只需根据需要更改 CTE 年份中的年份即可。

          with
          year as 
          (
            select '2013' year
          ),
          gender as (
            select 'Male' gender
            union all
            select 'Female' gender
          )
          select coalesce(g.gender,'Total') "Label", 
                 count(s.gender) "Value", 
                 y.year "Year"
            from gender g
            cross join year y
            left join student s
              on s.gender = g.gender
             and s.year = y.year
           group by grouping sets( (g.gender, y.year), (y.year) )
           order by case g.gender when 'Male' then 1 when 'Female' then 2 else 3 end
          ;
          

          完全标准化的数据模型可能同时具有学年和性别表,因此不需要 CTE。 (除非您真的想返回没有任何数据的年份的行)

          这是一个简单的 sqlfiddle 演示,没有学生 ID 和姓名,因为它们与手头的问题无关。

          【讨论】:

            【解决方案9】:

            您的请求看起来很简单,但它有两个复杂性。第一个是一行是其他两行的摘要。这建议在查询中使用rollupgrouping sets

            第二个是即使您没有数据也需要有值。这建议使用“驱动程序”子查询。这样的子查询在分配值之前定义输出中的所有行。您使用带有left outer join 的驱动程序表。

            未说明的要求可能是只提及一年一次。

            以下查询方法将年份的最终表格放在一起。然后左加入摘要,从那里提取值(如果有):

            with year as (
                  select 2013 as Year
                 )
            select driver.label, coalesce(s.value, 0) as Value, driver.Year
            from ((select 'Male' as label, year from year
                  ) union all
                  (select 'Female' as label, year from year
                  ) union all
                  (select 'Total' as label, year from year
                  )
                 ) driver left outer join
                 (select coalesce(Gender, 'Total') as Gender, year.year, count(*) as value
                  from Students cross join year
                  group by Gender with Rollup
                 ) s
                 on driver.year = s.year;
            

            这假设性别表示为“男性”和“女性”,并且数据中有一个名为 year 的列(没有样本输入或表格格式,人们必须猜测列名和样本值)。

            【讨论】:

            • 我喜欢你的解释,但你的实现有问题,我认为过于复杂。 “s”子查询缺少 group by 的年份,并且 total 错误地包括所有年份。外部连接条件 driver.gender 不存在(应该是 driver.label),并且 Total 行在年份永远不会匹配(2013 与 null)。有关更简单的工作查询,请参阅 my earlier answer
            • @dbenham 。 . .当我查看答案时,我正在寻找具有unionrollup 的答案,并且由于“分组ses”而一定错过了您的答案。似乎没有一个答案解决了正确的问题,这就是我回答的原因(然后试图简化答案,导致您注意到的问题)。我已经正式支持您的回答,因为这是适当的方法。
            【解决方案10】:

            这对我有用。但是,在没有数据的年份里,M 和 F 仍然无法显示 0:

            Select * from 
            (
            SELECT isnull (SUM(CASE WHEN gender = 'M' THEN 1 ELSE 0 END),0) as Male,
                   isnull(SUM(CASE WHEN gender = 'F' THEN 1 ELSE 0 END),0) as Female,
                   registeredYear as 'year'
            
            FROM student
            WHERE registeredDate.Year = 2013 //could be a variable
            group by registeredYear
             ) as s
            
             UNPIVOT
             ( 
            value FOR gender IN (Male, Female) 
             ) Sub
            

            【讨论】:

              【解决方案11】:

              你应该使用:

              select name, COUNT(*)as tot, 
                COUNT(case when details.gender='male' then 1 end) as male,
                COUNT(case when details.gender='female' then 1 end) as female 
                from details  group by name
              

              【讨论】:

                【解决方案12】:
                select sp.CLASS_Name , count(*) as total 
                     , sum( case when si.STDNT_GENDER = 1   then 1 else 0 end )    as Male
                     , sum( case when si.STDNT_GENDER = 0   then 1 else 0 end )    as Female
                  from SCHOOL_PLANE sp  inner join STUDENT_INFO si 
                on  sp.CLASS_ID=si.STDNT_CLASS_PLANE_ID group by sp.CLASS_Name
                
                -------
                select sp.CLASS_Name , count(*) as total 
                     , sum( case si.STDNT_GENDER  when  1   then 1 else 0 end )    as Male
                     , sum( case si.STDNT_GENDER  when  0   then 1 else 0 end )    as Female
                  from SCHOOL_PLANE sp  inner join STUDENT_INFO si 
                on  sp.CLASS_ID=si.STDNT_CLASS_PLANE_ID group by sp.CLASS_Name
                ------------
                select sp.CLASS_Name , count(*) as total 
                     , count( case when si.STDNT_GENDER = 1   then 1  end )    as Male
                     , count( case when si.STDNT_GENDER = 0   then 1  end )    as Female
                  from SCHOOL_PLANE sp  inner join STUDENT_INFO si 
                on  sp.CLASS_ID=si.STDNT_CLASS_PLANE_ID group by sp.CLASS_Name
                

                【讨论】:

                • 您可以通过在解决方案中添加简短描述来改进您的答案。
                【解决方案13】:

                选择 * 来自 (选择年份,计数(*)作为总数,总和(性别='M'然后1否则0结束的情况)作为男性, 总和(性别 ='F' 然后 1 否则 0 结束的情况)作为 mytable 中的女性 其中年份=2013 按性别、年份分组)
                不枢轴 (收入组件价值 对于收入组件类型(男性,女性,总计) )

                【讨论】:

                • 欢迎来到 Stack Overflow。在这里回答问题很好,但这只是一堆未格式化的代码。一个好的答案应该是整齐的格式,并包括它的工作原理以及它如何解决原始问题的解释。对这么老的问题和这么多答案的答案也应该为该主题增加新的见解。请edit你的回答来改进它。
                猜你喜欢
                • 2022-01-11
                • 1970-01-01
                • 1970-01-01
                • 2021-01-13
                • 2016-09-02
                • 2021-03-19
                • 1970-01-01
                • 2020-03-18
                • 1970-01-01
                相关资源
                最近更新 更多