获取连续值的总和

Posted

技术标签:

【中文标题】获取连续值的总和【英文标题】:Get running total of consecutive values 【发布时间】:2020-03-11 03:15:58 【问题描述】:

我有一个表格,按年份和月份存储用户报告的已完成活动的小时数。我在旧的 Oracle 数据库中有一个函数,我需要在 SQL Server 中复制它,它显示连续 0 小时报告的当前值。即当某人连续 3 个月没有报告小时数时,该数字将是 3,如果他们已经报告了给定月份,则计数器将重置为 0,以便仅计算连续报告为 0小时。

该表格始终预先填充用户的预期报告期数量。即如果用户预计报告 6 个月的小时数,我们在此表中创建 6 行,在 HOURS_REPORTED 字段中使用 NULL。

这是一个 PLSQL 示例。

FUNCTION FN_NIL_RPT_CNT ( PI_USER_ID IN NUMBER )
 RETURN NUMBER
 IS

 LV_NIL_CNT NUMBER := 0;

 CURSOR CR_DTLS IS
 SELECT YEAR
       ,MONTH
       ,HOURS_REPORTED
 FROM HOURS
 WHERE STATUS = 'CURRENT'
       AND USER_ID = PI_USER_ID
 ORDER BY TO_DATE(MONTH||YEAR, 'mmyyyy'); 

 BEGIN
   FOR LR_HR IN CR_DTLS
   LOOP
     EXIT WHEN TO_DATE(LR_HR.MONTH||LR_HR.YEAR,'mmyyyy') >= TRUNC(SYSDATE,'MM');

     IF LR_HR.HOURS_REPORTED IS NOT NULL
     THEN
       IF LR_HR.HOURS_REPORTED = 0
       THEN
         LV_NIL_CNT := LV_NIL_CNT + 1;
       ELSE
         LV_NIL_CNT := 0;
       END IF;
     ELSE 
       NULL; -- ignore
     END IF;
   END LOOP;
 END;
 RETURN LV_NIL_CNT;
END FN_NIL_RPT_CNT;

因此,如果光标包含以下内容,则预期结果将为 2,因为在 2019/10 年报告了小时数,这会将计数器重置为 0,并且随后的 2 个月报告了 0 小时。

【问题讨论】:

您想要报告的连续“0”小时数。作为您展示的示例,如果小时数为 1,0,0,1,0,0,则预期输出是什么 @GeorgeJoseph 假设这些数字按年份和月份的升序排列,则计数应显示为 2,因为过去两个月报告了 0,因为 3 个月前报告的 1 将重置计数到 0 【参考方案1】:

我的建议是使用直接sql,并丢失游标逻辑。任何时候它都会变得更高效、更快、更便宜

例如:以下查询可以获得 last_consecutive_count

CREATE TABLE dbo.t
(
    USER_ID INT
   ,YEAR INT
   ,MONTH INT
   ,HOURS_REPORTED INT
);

insert into dbo.t
VALUES
( 254, '2017', '12', 0 ), 
( 254, '2018', '01', 8 ), 
( 254, '2018', '02', 11 ), 
( 254, '2018', '03', 16 ), 
( 254, '2018', '04', 12 ), 
( 254, '2018', '05', 16 ), 
( 254, '2018', '06', 20 ), 
( 254, '2018', '07', 14 ), 
( 254, '2018', '08', 12 ), 
( 254, '2018', '09', 11 ), 
( 254, '2018', '10', 22 ), 
( 254, '2018', '11', 10 ), 
( 254, '2018', '12', 6 ), 
( 254, '2019', '01', 11 ), 
( 254, '2019', '02', 12 ), 
( 254, '2019', '03', 5 ), 
( 254, '2019', '04', 12 ), 
( 254, '2019', '05', 10 ), 
( 254, '2019', '06', 9 ), 
( 254, '2019', '07', 10 ), 
( 254, '2019', '08', 9 ), 
( 254, '2019', '09', 0 ), 
( 254, '2019', '10', 4 ), 
( 254, '2019', '11', 8 ), 
( 254, '2019', '12', 0 ), 
( 254, '2020', '01', 0 ), 
( 254, '2020', '02', 5 ),
( 254, '2020', '03', 7 )



WITH data
AS (SELECT *
          ,CAST(CONCAT(YEAR, '-', MONTH, '-01') AS DATE) AS dt
          ,ROW_NUMBER() OVER (ORDER BY YEAR desc, MONTH desc) AS rnk
          ,ROW_NUMBER() OVER (partition by case when hours_reported=0 then 1 end ORDER BY YEAR desc, MONTH desc) AS rnk2
          ,DATEADD(MONTH, -ROW_NUMBER() OVER (ORDER BY YEAR, MONTH), CAST(CONCAT(YEAR, '-', MONTH, '-01') AS DATE)) AS grp
    FROM dbo.t
    WHERE USER_ID = 254 /* you can parameterize this in your sql server function*/
    )
   ,last_val
    as(SELECT *
          ,case when hours_reported<>0 then rnk else rnk-rnk2 end as val1
          ,rank() over(order by case when hours_reported<>0 then rnk else rnk-rnk2 end) as rnk_min
      FROM data
     )
select count(*) as last_consec_cnt
  from last_val
 where rnk_min=1


+----------------------+
| last_consecutive_cnt |
+----------------------+
|                    1 |
+----------------------+

你可以把它放在一个函数中,然后将 user_id 传递给表..

这是一个 db fiddle 链接。 https://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=9ac60e62a99e2465cc8e979ae9f29f7c

【讨论】:

我绝对不想使用游标,这对你来说只是老派的 Oracle 代码 :) 我不想要最后的连续计数,我想要当前的连续计数。我已经改变了你的小提琴。如果最后报告的值不是 0,那么它不会按预期工作。 dbfiddle.uk/… :-) 很酷,我已经编辑了答案以适应结果仅限于当前日期的情况 你要最后一个连续计数,不管它是否为0? 不是真的,我需要知道当前错过的报告总数。因此,如果他们没有报告 3 个月的小时数,而是报告了本月/当前月的小时数,那么该值应该为 0,因为他们已经报告了当月的小时数。我已经用我真实数据集中的一个实际案例更新了小提琴:dbfiddle.uk/… 我已经更新了答案,请查看以下链接dbfiddle.uk/…

以上是关于获取连续值的总和的主要内容,如果未能解决你的问题,请参考以下文章

如何在飞镖列表中获取重复值的总和?

如何从此查询中获取“temp”列值的总和?

如何使用Javascript获取输入值的总和?

SQL Server - 重叠数据的累积总和 - 获取总和达到给定值的日期

获取总和小于或等于数据集中值的记录

SQL:获取行列值的总和