获取连续值的总和
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/…以上是关于获取连续值的总和的主要内容,如果未能解决你的问题,请参考以下文章