使用 Count 计算 SQL 中的百分比时未获得预期输出

Not getting expected output when calculating percentage in SQL using Count

我是 运行 一个查询,但它没有输出我期望的结果。我有下面的 table:

------------------------
security_ID | Date | Rep 
------------------------
2256        |202001|  0
2257        |202002|  0
2257        |202003|  0
2256        |202002|  1
2256        |202003|  2
2257        |202003|  1

我基本上是在尝试找出列 Rep 从一个日期到下一个日期从 0 变为 1 的情况百分比,而前一个日期的 Rep 为 0,对于给定的 security_IDDates 的差异应为 1(例如,202002-202001 = 1。此处日期为整数)用于计算。 这里,对于security_ID = 2256Percent = 100202001202002Rep从0变为1,而在table 22560 的行数为 1。百分比的公式为:

百分比 =(Rep_current = 1 且 Rep_prev = 0 的案例数)/(Rep_prev = 0 的案例数)* 100

对于 security_ID = 2257,百分比 = 1/2 * 100 = 50

例如,我希望输出为:

----------------------------------
security_ID | Date | Rep | Percent
----------------------------------
2256        |202001|  0  |  100
2257        |202002|  0  |  50 
2257        |202003|  0  |  50 
2256        |202002|  1  |  100 
2256        |202003|  2  |  100
2257        |202003|  1  |  50

我尝试按如下方式执行此操作:

SELECT security_ID, Date, Rep, 
(COUNT(CASE WHEN Rep_prev = 0 and Rep = 1 then 1 else 0 end)/count(CASE WHEN Rep_prev = 0 then 1 else 0 end) * 100) as "Percent"
from
(
    select t1.security_id,t1.date,t1.rep,
           coalesce(t2.rep,0) as Rep_prev
      from mytable t1
      left join mytable t2
        on t1.security_id = t2.security_id
       and t2.date = t1.date - 1
    )
GROUP BY SECURITY_ID, Date, Rep

但我得到的输出是:

----------------------------------
security_ID | Date | Rep | Percent
----------------------------------
2256        |202001|  0  |  100
2257        |202002|  0  |  100 
2257        |202003|  0  |  100 
2256        |202002|  1  |  100 
2256        |202003|  2  |  100
2257        |202003|  1  |  100

不太确定我的逻辑哪里错了。

如果您想了解更多信息,请告诉我,因为很难表达这个想法。

你可以使用下面的逻辑(使用Windows函数)-

Select mt.security_id, mt.Date, mt.Rep, (numerator*100)/denominator As "Percent"
from
  (Select security_id,
       SUM(case when Rep_curr = 1 and coalesce(Rep_prev,-99) = 0 then 1 else 0 end) As numerator,
       SUM(case when coalesce(Rep_prev,-99) = 0 then 1 else 0 end) As denominator
    from
     (Select security_id, 
             Rep As Rep_curr,
             lag(Rep,1) over(partition by security_id order by Date) As Rep_prev
      from my_table) t 
   where t.Rep_prev is not NULL
   group by security_id) tab
   join my_table mt 
   on tab.security_id = mt.security_id;

这是一个数据库 fiddle link,演示了它在 SQL 服务器 - https://dbfiddle.uk/?rdbms=sqlserver_2014&fiddle=8654f0242bbed911f1ed63b795281bac 中的工作原理。上述语法也适用于 Sybase。

编辑: 如果我要使用你的方法,我会这样做 -

SELECT mt.*, Tab1.Perct As "Percent"
FROM
    (SELECT security_ID,
            (SUM(CASE WHEN Rep_prev = 0 and Rep = 1 then 1 else 0 end) * 100/SUM(CASE WHEN Rep_prev = 0 then 1 else 0 end)) as Perct
    from
       (
        select t1.security_id,t1.date,t1.rep,
               coalesce(t2.rep,0) as Rep_prev
          from mytable t1
          left join mytable t2
            on t1.security_id = t2.security_id
           and t2.date = t1.date - 1
          where  t2.rep IS NOT NULL
        ) t
    GROUP BY SECURITY_ID ) Tab1
    JOIN mytable mt
    ON TAB1.security_ID = mt.security_ID;

使用 window 函数,如下所示:

select t.*,
       (sum(case when prev_rep = 0 and rep = 1 then 100.0 else 0 end) over (partition by security_id) /
        sum(case when rep = 0 then 1 else 0 end) over (partition by security_id)
       ) as precent
from (select t.*,
             lag(rep) over (partition by security_id order by date) as prev_rep
      from mytable t
     ) t;

如果您使用的 Sybase 版本不支持 window 函数,那么您可以使用 security_id 计算此

select security_id,
       (sum(case when prev_rep = 0 and rep = 1 then 100.0 else 0 end) /
        sum(case when rep = 0 then 1 else 0 end)
       ) as precent
from (select t.*,
             (select top (1) t2.rep
              from mytable t2
              where t2.security_id = t.security_id and
                    t2.date < t.date
              order by t2.date desc
             ) as prev_rep
      from mytable t
     ) t
group by security_id;

然后您可以 join 将其返回给您的 table 如果您每行都需要它。

编辑:

如果您每个月都有行,那么也许这行得通:

select security_id,
       (sum(case when prev_rep = 0 and rep = 1 then 100.0 else 0 end) /
        sum(case when rep = 0 then 1 else 0 end)
       ) as precent
from (select t.*, tprev.rep as prev_rep
      from mytable t left join
           mytable tprev
           on t.security_id = tprev.security_id and
              convert(date, tprev.date + '01') = dateadd(month, -1, convert(date, t.date + '01'))
     ) t
group by security_id;

注意:这假定 date 是一个字符串。将其存储为适当的 date 会简化逻辑。