SQL百分比动态计算

时间:2017-10-30 15:40:14

标签: sql intersystems-cache

如何在SQL中动态计算百分比?

我们假设您有一个名为Classes的下表:

ClassSession       StudentName
---------------------------------
Evening            Ben
Morning            Chris
Afternoon          Roger
Evening            Ben
Afternoon          Ben
Morning            Roger
Morning            Ben
Afternoon          Chris

让我们对Ben说,我期待

Evening = 50 %
Afternoon = 25%
Morning = 25%

对于克里斯我期待

Morning = 50%
Afternoon = 50%
Evening = 0 %

所以ClassSession(三个会话)应该是不变的以进行比较

到目前为止,我已经尝试了以下SQL语句:

Select 
    ClassSession,
    (Count(ClassSession) * 100 / (Select Count(*) From Classes)) as Percentage
From 
    Classes
Where 
    StudentName = 'Chris'
Group By 
    ClassSession

3 个答案:

答案 0 :(得分:2)

困难的部分是让那些在特定课程中没有任何课程的学生出现零。

这是PARTITION外部联接的作业。

select c.studentname, 
       s.classsession, 
       round(ratio_to_report(count(c.classsession)) 
            over ( partition by c.studentname),2) pct
from c partition by ( studentname ) 
   right outer join ( SELECT distinct classsession from c ) s 
          on s.classsession = c.classsession
group by c.studentname, s.classsession
order by c.studentname, s.classsession;

请注意联接中的PARTITION关键字。这告诉Oracle为每个分区执行外连接。因此,如果给定的studentname没有classsession,请为该学生添加。{/ p>

此外,ratio_to_report是计算百分比的好功能。

以下是一个完整的示例,包含数据:

with c (ClassSession, StudentName) AS ( 
SELECT 'Evening',            'Ben' FROM DUAL UNION ALL
SELECT 'Morning',            'Chris' FROM DUAL UNION ALL
SELECT 'Afternoon',          'Roger' FROM DUAL UNION ALL
SELECT 'Evening',            'Ben' FROM DUAL UNION ALL
SELECT 'Afternoon',          'Ben' FROM DUAL UNION ALL
SELECT 'Morning',            'Roger' FROM DUAL UNION ALL
SELECT 'Morning',            'Ben' FROM DUAL UNION ALL
SELECT 'Afternoon',          'Chris' FROM DUAL)
select c.studentname, 
       s.classsession, 
       round(ratio_to_report(count(c.classsession)) 
            over ( partition by c.studentname),2) pct
from c partition by ( studentname ) 
   right outer join ( SELECT distinct classsession from c ) s on s.classsession = c.classsession
group by c.studentname, s.classsession
order by c.studentname, s.classsession;


╔══════════════════════════════════════════════════════════════════╗
║ STUDENTNAME CLASSSESSION PCT                                     ║
╠══════════════════════════════════════════════════════════════════╣
║ ----------- ------------ --------------------------------------  ║
║ Ben         Afternoon                                      0.25  ║
║ Ben         Evening                                         0.5  ║
║ Ben         Morning                                        0.25  ║
║ Chris       Afternoon                                       0.5  ║
║ Chris       Evening                                           0  ║
║ Chris       Morning                                         0.5  ║
║ Roger       Afternoon                                       0.5  ║
║ Roger       Evening                                           0  ║
║ Roger       Morning                                         0.5  ║
╚══════════════════════════════════════════════════════════════════╝

答案 1 :(得分:1)

一种方法使用条件聚合和窗口函数:

Select ClassSession,
       (sum(case when StudentName = 'Chris' then 100.0 else 0 end) /
        sum(sum(case when StudentName = 'Chris' then 100.0 else 0 end)) over ()
       ) as Percentage
From Classes
Group By ClassSession;

这将确保事件零显示。

答案 2 :(得分:0)

这是使用SQL Server 2008及更高版本的传统方式。 (在以后的版本中,可能有更方便的方法使用统计窗口函数来编写它。)

对于所有至少有一名学生的班级,这将返回至少一门课程中所有学生的数据。如果表很大,请取消注释where子句以一次为一个学生获取数据

首先,测试数据:

CREATE TABLE #Test
 (
   ClassSession  varchar(20)  not null
  ,StudentName   varchar(20)  not null
 )

INSERT #Test values 
  ('Evening', 'Ben')
 ,('Morning', 'Chris')
 ,('Afternoon', 'Roger')
 ,('Evening', 'Ben')
 ,('Afternoon', 'Ben')
 ,('Morning', 'Roger')
 ,('Morning', 'Ben')
 ,('Afternoon', 'Chris')


SELECT *
 from #Test

查询:

WITH cteClasses
 as (--  First, get the list of classes
     SELECT distinct ClassSession
      from #Test
     )
 ,cteStudents
  as (--  Next, get a list of all students
      SELECT
         StudentName
        ,count(*) * 1.00  ClassCount
       from #Test
       --where StudenName = @StudentParameter
       group by StudentName
     )
 --  Mush them all together, and...
 SELECT
   st.StudentName
  ,cl.ClassSession
  ,count(te.StudentName) / st.ClassCount * 100  Percentage
 from cteStudents st
  cross join cteClasses cl
  left join #Test te
   on te.ClassSession = cl.ClassSession
    and te.StudentName = st.StudentName
 group by
   st.StudentName
  ,cl.ClassSession
  ,st.ClassCount
 order by 
   st.StudentName
  ,cl.ClassSession