You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于间断数据按日期和状态统计对象数量的技术问询

按日期统计间断状态对象的数量

问题背景

需要按日期统计处于特定状态的对象(如设备、任务、账单等)数量,但状态记录是间断的——仅在状态变更时才有记录。直接用GROUP BY无法得到正确结果,因为某一天的状态数量需要继承历史未变更的对象状态。例如3月2日仅一条新记录,但正确统计需要包含3月1日未变更的对象状态。

示例数据

以下是最小化复现问题的SQL代码:

SET NOCOUNT ON
-->>-- minimum problem to solve  -- count by status for a specific day
IF OBJECT_ID('tempdb..#d') IS NOT NULL DROP TABLE #d
CREATE TABLE #d (ndx SMALLINT IDENTITY(1,1), id TINYINT, dt DATE, status CHAR(10) )

INSERT INTO #d (id, dt, status)
VALUES
  ( 1, '20230301' , 'on'        )
, ( 3, '20230301' , 'off'       )
, ( 2, '20230302' , 'on'        )
, ( 3, '20230303' , 'off'       )
, ( 3, '20230305' , 'on'        )
, ( 1, '20230308' , 'off'       )
, ( 2, '20230308' , 'off'       )
, ( 1, '20230310' , 'off'       )
, ( 2, '20230311' , 'off'       )
, ( 1, '20230312' , 'off'       )
, ( 3, '20230312' , 'off'       )
, ( 2, '20230313' , 'on'        )
, ( 1, '20230314' , 'on'        )
, ( 3, '20230314' , 'off'       )
, ( 3, '20230316' , 'off'       )
, ( 2, '20230320' , 'on'        )
, ( 1, '20230321' , 'off'       )

SELECT * FROM #d d ORDER BY id, dt

IF OBJECT_ID('tempdb..#c') IS NOT NULL DROP TABLE #c
CREATE TABLE #c ( calendardt DATE )
INSERT INTO #c(calendardt)
VALUES
  ('2023-03-01 '), ('2023-03-02 '), ('2023-03-03 '), ('2023-03-04 '), ('2023-03-05 ')
, ('2023-03-06 '), ('2023-03-07 '), ('2023-03-08 '), ('2023-03-09 '), ('2023-03-10 ')
, ('2023-03-11 '), ('2023-03-12 '), ('2023-03-13 '), ('2023-03-14 '), ('2023-03-15 ')
, ('2023-03-16 '), ('2023-03-17 '), ('2023-03-18 '), ('2023-03-19 '), ('2023-03-20 ')
, ('2023-03-21 '), ('2023-03-22 '), ('2023-03-23 '), ('2023-03-24 '), ('2023-03-25 ')

SELECT * FROM #c UNION ALL SELECT * FROM #c ORDER BY calendardt

SELECT *
FROM #c c
LEFT JOIN #d d ON d.dt = c.calendardt
ORDER BY c.calendardt, d.id

预期结果

calendardt  [status]    [count]
2023-03-01  on          1
2023-03-01  off         1
2023-03-02  on          2
2023-03-02  off         1
2023-03-03  on          2
2023-03-03  off         1
2023-03-04  on          2
2023-03-04  off         1
2023-03-05  on          3
2023-03-05  off         0
2023-03-06  on          3
2023-03-06  off         0
2023-03-07  on          3
2023-03-07  off         0
2023-03-08  on          1
2023-03-08  off         2
2023-03-09  on          1
2023-03-09  off         2
2023-03-10  on          1
2023-03-10  off         2
2023-03-11  on          1
2023-03-11  off         2
2023-03-12  on          1
2023-03-12  off         2
2023-03-13  on          0
2023-03-13  off         3
2023-03-14  on          0
2023-03-14  off         3
2023-03-15  on          0
2023-03-15  off         3
2023-03-16  on          0
2023-03-16  off         3

解决方案

核心思路是先为每个对象的每条状态记录计算其有效结束日期(即下一次状态变更的前一天),然后将这些状态有效期与日历表关联,最后按日期和状态统计数量。

完整SQL代码

SET NOCOUNT ON;

-- 第一步:为每条状态记录计算有效结束日期基准
WITH StatusPeriods AS (
    SELECT 
        id,
        dt AS start_dt,
        status,
        -- 获取下一次状态变更日期,若无则用日历表最大日期
        LEAD(dt, 1, (SELECT MAX(calendardt) FROM #c)) OVER (PARTITION BY id ORDER BY dt) AS next_status_dt
    FROM #d
),
-- 第二步:生成状态的有效区间(start_dt 到 next_status_dt - 1)
StatusRanges AS (
    SELECT 
        id,
        start_dt,
        DATEADD(DAY, -1, next_status_dt) AS end_dt,
        status
    FROM StatusPeriods
),
-- 第三步:关联日历表与状态区间,匹配每个日期对应的对象状态
DailyStatus AS (
    SELECT 
        c.calendardt,
        sr.status
    FROM #c c
    CROSS JOIN (SELECT DISTINCT id FROM #d) obj
    LEFT JOIN StatusRanges sr 
        ON obj.id = sr.id 
        AND c.calendardt BETWEEN sr.start_dt AND sr.end_dt
    -- 筛选到预期结果的日期范围(可按需调整)
    WHERE c.calendardt <= '2023-03-16'
)
-- 第四步:按日期和状态统计数量,确保所有状态都显示
SELECT 
    calendardt,
    s.status,
    COUNT(CASE WHEN ds.status = s.status THEN 1 END) AS [count]
FROM DailyStatus ds
CROSS JOIN (SELECT DISTINCT status FROM #d) s
GROUP BY calendardt, s.status
ORDER BY calendardt, s.status;

代码说明

  • StatusPeriods:用LEAD()窗口函数获取每个对象的下一次状态变更时间,确定当前状态的截止基准。
  • StatusRanges:将截止基准转换为当前状态的最后有效日期(下一次变更前一天)。
  • DailyStatus:将每个对象与日历表交叉关联,匹配该日期下对象的有效状态。
  • 最后通过交叉所有状态值,确保即使某状态在当天无对应对象,也能显示数量为0的记录。

内容的提问来源于stack exchange,提问作者jim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 14:12:56