zoukankan      html  css  js  c++  java
  • 分段统计与Oracle的分析函数、逻辑判断等知识点的综合运用

    重点部分:TOTAL层 

    项目要求:

    统计每个巡检员(USER_ID)当前月的签到率及查询相关字段

    签到率公式:以巡检员为单位,

    (当月至今天为止签到的所有点/该月巡检点的总个数)=(b.Point/a.Total)

    TOTAL相关更多要求:

    ①在使用过程中,巡检点个数会根据实际情况进行变更,例如2014年1月15日管理员给某一位巡检员安排5个点,10天后增加了2个点,即2014年1月24日以后按照7个点来统计,所以,如果当月更改多次的话,这个月会分成许多段。

    ②每一次更改有一个批次号,对应表T_SIGN_POINTS的POINT_DATE字段,这个字段的值设置为明天生效,即今日更改,明天生效。

    ③TOTAL公式:

    假设有以下已知条件:

    假设今天是2014年4月25日,USER_ID=1249,统计2014年4月的该巡检员应该巡查的点的总个数,T_SIGN_POINTS中相关批次有Batch2014.3.29=6个、Batch2014.4.5=8个,Batch2014.4.12=11个、Batch2014.4.26日=5个

    则TOTAL的数学计算应该为:6个×4天+8个×11天+11个×13天=255个(另有其它一些边界情况类似)

    这里需要考虑到多种可能性,即便只使用最理想的情况,将上面这个公式完全使用SQL语言完成,也是一个挺复杂的过程,相比之下,查询需要的字段后在后台做计算难度会降低,不过这种写法可以大幅提升对Oracle或者SQL语言的运用和理解,不排除Oracle有更合适的函数或更简洁的方法来解决类似问题。 

    相关表及字段情况:

    表T_SIGN_RECORDS:记录该巡检员已签到的点

    表T_SIGN_POINTS:记录给每个巡检员安排的巡检点

    完整SQL语句:

      1 select *
      2   from (select a.*,
      3                case a.IS_LOGIN
      4                  when 1 then
      5                   '已登录'
      6                  when 0 then
      7                   '未登录'
      8                  else
      9                   '未登录'
     10                end as WEB_STATUS,
     11                case a.MOBILE_LOGIN
     12                  when 1 then
     13                   '已登录'
     14                  when 0 then
     15                   '未登录'
     16                  else
     17                   '未登录'
     18                end as MOBILE_STATUS,
     19                b.name as dep_name,
     20                b.parent_id,
     21                b.dep_level,
     22                c.name as role_name,
     23                c.order_index,
     24                c.function_no
     25           from t_user a, t_department b, t_role c
     26          where a.dep_id = b.id
     27            and a.role_id = c.id
     28            and IS_PATROL = 1) x
     29   left join (select a.user_id,
     30                     (case
     31                       when b.point is null then
     32                        0
     33                       else
     34                        b.point
     35                     end) || '/' || (a.total) as rate
     36                from (select user_id, sum(PartNum) as total   /*【TOTAL第2层】开始*/
     37                        from (select user_id,    /*【TOTAL第1层】开始*/
     38                                     tday,
     39                                     batchnum,
     40                                     batchMon,
     41                                     batchDay,
     42                                     seg_Next,
     43                                     decode(seg_next,
     44                                            null,
     45                                            decode(greatest(batchday, tday),
     46                                                   tday,
     47                                                   (tday - batchday + 1) *
     48                                                   batchnum,
     49                                                   batchday,
     50                                                   0,
     51                                                   null),
     52                                            decode(greatest(seg_next, tday),
     53                                                   tday,
     54                                                   (seg_next - batchday) *
     55                                                   batchnum,
     56                                                   seg_next,
     57                                                   (tday - batchday + 1) *
     58                                                   batchnum,
     59                                                   null)) as PartNum
     60                                from (SELECT a.user_id,
     61                                             a.tday,
     62                                             a.batchnum,
     63                                             a.batchMon,
     64                                             a.batchDay,
     65                                             lead(a.batchDay, 1, null) over(PARTITION BY a.user_id order by a.batchDay, a.batchmon) as seg_Next
     66                                        FROM (select t.user_id,    /*【TOTAL第0层】开始*/
     67                                                     to_char((SELECT EXTRACT(MONTH FROM
     68                                                                            sysdate)
     69                                                               FROM DUAL)) as TMon,
     70                                                     (SELECT TRUNC(SYSDATE)
     71                                                        FROM DUAL) as TDay,
     72                                                     decode(sign(months_between(trunc(TO_DATE(t.points_date,
     73                                                                                              'YYYY-MM-DD'),
     74                                                                                      'MM'),
     75                                                                                trunc(sysdate,
     76                                                                                      'MM'))),
     77                                                            0,
     78                                                            (select (trunc(TO_DATE(t.points_date,
     79                                                                                   'YYYY-MM-DD'),
     80                                                                           'MM') + 1)
     81                                                               from dual),
     82                                                            -1,
     83                                                            (select trunc(sysdate,
     84                                                                          'MM')
     85                                                               from dual),
     86                                                            1,
     87                                                            (select (trunc(TO_DATE(t.points_date,
     88                                                                                   'YYYY-MM-DD'),
     89                                                                           'MM') + 2)
     90                                                               from dual)) as batchMon,
     91                                                     decode(sign(months_between(trunc(TO_DATE(t.points_date,
     92                                                                                              'YYYY-MM-DD'),
     93                                                                                      'MM'),
     94                                                                                trunc(sysdate,
     95                                                                                      'MM'))),
     96                                                            -1,
     97                                                            (select trunc(sysdate,
     98                                                                          'MM')
     99                                                               from dual),
    100                                                            TO_DATE(t.points_date,
    101                                                                    'YYYY-MM-DD')) as batchDay,
    102                                                     count(t.user_id) as BatchNum
    103                                                from T_SIGN_POINTS t
    104                                               where (to_date(t.points_date,
    105                                                              'YYYY-MM-DD') -
    106                                                     (select trunc(sysdate, 'MM')
    107                                                         from dual) >= 0)
    108                                                  or (t.points_date =
    109                                                     (select max(points_date) as EarlyMax
    110                                                         from T_SIGN_POINTS
    111                                                        where USER_ID = t.user_id
    112                                                          and (to_date(points_date,
    113                                                                       'YYYY-MM-DD') <
    114                                                              (select trunc(sysdate,
    115                                                                             'MM')
    116                                                                  from dual))
    117                                                        GROUP BY USER_ID))
    118                                               group by t.user_id, t.points_date
    119                                               order by t.user_id desc,
    120                                                        batchDay  desc,
    121                                                        batchmon  desc) A /*【TOTAL第0层】结束*/
    122                                       order by user_id  desc,
    123                                                batchday desc,
    124                                                batchmon desc)) /*【TOTAL第1层】结束*/
    125                       group by user_id
    126                       order by user_id desc) a    /*【TOTAL第2层】结束*/
    127                left join (select count(*) as point, user_id
    128                            from t_sign_records sr
    129                           where to_char(sign_time, 'yyyy-mm') = '2014-05'
    130                           group by user_id) b
    131                  on a.user_id = b.user_id) y
    132     on x.id = y.user_id
    133  order by order_index, user_id

    注释:

    【外围层】连接到:用户表(T_USER)、部门表(T_DEPARTMENT)、角色表(T_ROLE)查询一些字段 ,值得注意的是CASE WHEN THEN ELSE END和显示格式“|| '/' ||”的使用

    【POINT层】一个简单的count函数的使用

    【TOTAL层】是重点,由多层嵌套而成,接下来是各层的结果图片和知识点:

    【TOTAL第0层】

    结果图片:

    SQL功能:查询并统计与当月相关的批次及各批次的点的个数

    BatchNum为该批次的点的个数,

    TMon为当月的月份,

    BatchMon为批次号所在的月份,这个字段的值可以区分批次号是当月的还是本月之前的最大批次号,为的是辅助lead函数进行排序,如果批次号在当月,结果为当月的第2天,如果在这个月之前,返回当月的第1天

    【TOTAL第1层】

    结果图片:

    TOTAL第1层内层结果

    TOTAL第1层外层结果:计算每一段的签到点的总个数

    SQL功能:主要增加SEG_NEXT字段,将下一个批次号的日期做为SEG_NEXT

    lead(a.batchDay, 1, 0) over(PARTITION BY a.user_id order by a.batchDay, a.batchmon) as seg_Next

    PARTITION以a.user_id为区域执行lead函数,按照a.batchDay和a.batchmon排序,这里可看出batchmon字段的意义:如果本月在2014年4月1号有批次号,就把小于本月的最大批次号放在4月1号批次号的下面,这样,结果图片中第9行batchDay和SEG_NEXT就都是都是1,两段的差值是0,不会影响后续的结果。

    【TOTAL第2层】

    结果图片

    SQL知识点:

    Decode函数:

    decode(column,if_value,value,elseif_value,value,default_value);

    Decode函数可以与sigh函数和greatest函数结合使用,其中与greatest函数结合使用的时候,要注意参数的顺序和if_value和value的顺序,因为greatest的参数存在相等的情况,当参数相等的时候,会执行第一个if_value的value

     2014-05-09

      上面的SQL只能用在统计当月的情况,而且TMon和total最外层where语句有bug,后来项目中增加了一个按时间段查询的功能,将这些bug修正并总结了一个任意时间段通用版本。

      接下来的这个SQL可用于任意时间段的情况,只需传入合适的参数即可,其中StartTime对应时间段的起点、EndTime对应时间段的终点,建议格式‘YYYY-MM-DD’,目前的代码使用了“+StartTime+”的形式,这样直接就可以复制粘贴到Flex的SQL字符串中使用,只需变量名一致即可。

    select user_id, sum(PartNum) as total
      from (select user_id,
                   tday,
                   batchnum,
                   AidSort,
                   batchDay,
                   seg_Next,
                   decode(seg_next,
                          null,
                          decode(greatest(batchday, tday),
                                 tday,
                                 (tday - batchday + 1) * batchnum,
                                 batchday,
                                 0,
                                 null),
                          decode(greatest(seg_next, tday),
                                 tday,
                                 (seg_next - batchday) * batchnum,
                                 seg_next,
                                 (tday - batchday + 1) * batchnum,
                                 null)) as PartNum
              from (SELECT a.user_id,
                           a.tday,
                           a.batchnum,
                           a.AidSort,
                           a.batchDay,
                           lead(a.batchDay, 1, null) over(PARTITION BY a.user_id order by a.batchDay, a.AidSort) as seg_Next
                      FROM (select t.user_id,
                                   TO_DATE('"+EndTime+"', 'YYYY-MM-DD') as TDay,
                                   decode((select (case
                                                   when TO_DATE(t.points_date,
                                                                'YYYY-MM-DD') <
                                                        (to_date('"+StartTime+"',
                                                                 'YYYY-MM-DD')) then
                                                    -1
                                                   when TO_DATE(t.points_date,
                                                                'YYYY-MM-DD') >
                                                        TO_DATE('"+EndTime+"',
                                                                'YYYY-MM-DD') then
                                                    1
                                                   else
                                                    0
                                                 end)
                                            from dual),
                                          -1,
                                          'AS1',
                                          0,
                                          'AS2',
                                          'AS3') as AidSort,
                                   decode((select (case
                                                   when TO_DATE(t.points_date,
                                                                'YYYY-MM-DD') <
                                                        (TO_DATE('"+StartTime+"',
                                                                 'YYYY-MM-DD')) then
                                                    -1
                                                   when TO_DATE(t.points_date,
                                                                'YYYY-MM-DD') >
                                                        TO_DATE('"+EndTime+"',
                                                                'YYYY-MM-DD') then
                                                    1
                                                   else
                                                    0
                                                 end)
                                            from dual),
                                          -1,
                                          TO_DATE('"+StartTime+"', 'YYYY-MM-DD'),
                                          TO_DATE(t.points_date, 'YYYY-MM-DD')) as batchDay,
                                   count(t.user_id) as BatchNum
                              from T_SIGN_POINTS t
                             where (t.points_date =
                                   (select min(points_date) as FutureMin
                                       from T_SIGN_POINTS
                                      where USER_ID = t.user_id
                                        and (to_date(points_date, 'YYYY-MM-DD') >
                                            TO_DATE('"+EndTime+"', 'YYYY-MM-DD'))
                                      GROUP BY USER_ID))
                                or (to_date(t.points_date, 'YYYY-MM-DD') between
                                   TO_DATE('"+StartTime+"', 'YYYY-MM-DD') and
                                   TO_DATE('"+EndTime+"', 'YYYY-MM-DD'))
                                or (t.points_date =
                                   (select max(points_date) as EarlyMax
                                       from T_SIGN_POINTS
                                      where USER_ID = t.user_id
                                        and (to_date(points_date, 'YYYY-MM-DD') <
                                            TO_DATE('"+StartTime+"', 'YYYY-MM-DD'))
                                      GROUP BY USER_ID))
                             group by t.user_id, t.points_date
                             order by t.user_id desc, batchDay desc, AidSort desc) A
                     order by user_id desc, batchday desc, AidSort desc))
     group by user_id
     order by user_id desc

    这段SQL代码只是Total层(完整代码中已重命名为a层),如果想嵌入到最上面完整的代码,只需将完整代码的最后的b层更改为如下,其它不变

     left join (select count(*) as point, user_id
                             from t_sign_records sr
                            where trunc(sign_time) between
                                  TO_DATE('2014-05-05', 'YYYY-MM-DD') and
                                  TO_DATE('2014-05-09', 'YYYY-MM-DD')
                            group by user_id) b
    ‖==========钟于原创 乐于分享 宁静致远 毋忆典藏==========‖
  • 相关阅读:
    Idea打包问题
    centos问题总结
    Linux CentOS7 系统目录详解
    centos下修改文件后如何保存退出
    利用windows上的VMware安装CentOS7
    VMware安装系统出现Operating System not found 解决方案
    mybatis 0 变成null问题
    Shiro权限前端调用302重定向
    java版本
    产品画原型工具放入到托管平台
  • 原文地址:https://www.cnblogs.com/tingshuixuan2012/p/SQL_SubsectionStatistics.html
Copyright © 2011-2022 走看看