2016-10-11 51 views
5

我有這樣的數據:活動狀態和2012

CREATE TABLE #student 
(
    student_id INT, 
    status  VARCHAR(50), 
    created_dt DATE 
) 

CREATE TABLE #student_status_history 
(
    student_id  INT, 
    from_status  VARCHAR(50), 
    to_status   VARCHAR(50), 
    status_changed_dt DATE 
) 

INSERT INTO #student (student_id, status, created_dt) 
VALUES (1, 'Active', '2016-10-02'), 
     (2, 'Active', '2016-10-02'), 
     (3, 'Active', '2016-10-02') 

SELECT * 
FROM #student 

在10月5日student2狀態更新爲inactive

UPDATE #student 
SET status = 'Inactive' 
WHERE student_id = 2 

INSERT INTO #student_status_history (student_id, from_status, to_status, status_changed_dt) 
VALUES (2, 'Active', 'Inactive', '2016-10-05') 

SELECT * 
FROM #student 

SELECT * 
FROM #student_status_history 

在10月8日student2狀態更新爲active

UPDATE #student 
SET status = 'Active' 
WHERE student_id = 2 

INSERT INTO #student_status_history (student_id, from_status, to_status, status_changed_dt) 
VALUES (2, 'InActive', 'Active', '2016-10-08') 

10月9日我創建d另一名學生:

INSERT INTO #student (student_id, status, created_dt) 
VALUES (4, 'Active', '2016-10-09') 

10月10日我在表中列出了這些數據。

select * from #student 
    select * from #student_status_history 

使用上面的表格

我應該產生從十月1日至10月10日活動的學生爲在該日10月10日的一份報告

輸出應該如下

Date     ActiveCount 
    -----------   ----------- 
    2016-10-01     0 
    2016-10-02     3 
    2016-10-04     3 
    2016-10-05     2 
    2016-10-06     2 
    2016-10-07     2 
    2016-10-08     3 
    2016-10-09     4 
    2016-10-10     4 
+2

這是什麼邏輯爲'2016年10月4日3' – TheGameiswar

回答

12

這裏有一種方法可以做到這一點

你需要一個calendar表與日期列表。我已使用Recursive CTE生成日期範圍之間的日期。

;WITH calendar 
    AS (SELECT dates = CONVERT(DATETIME, '2016-10-01') 
     UNION ALL 
     SELECT dates = Dateadd(DAY, 1, dates) 
     FROM calendar 
     WHERE dates < '2016-10-10') 
SELECT c.dates, 
     Count(s.created_dt) AS ActiveCount 
FROM calendar c 
     LEFT JOIN #student s 
       ON s.created_dt <= c.dates 
WHERE NOT EXISTS (SELECT 1 
        FROM #student_status_history sh 
        WHERE sh.student_id = s.student_id 
        HAVING c.dates BETWEEN Min(CASE 
               WHEN from_status = 'active ' 
                AND to_status = 'Inactive' THEN status_changed_dt 
               END) AND Max(CASE 
                  WHEN to_status = 'active ' 
                    AND from_status = 'Inactive' THEN Dateadd(dd, -1, status_changed_dt) 
                  END)) 
GROUP BY c.dates 
OPTION (MAXRECURSION 0) 

結果:

╔═════════════════════════╦═════════════╗ 
║   dates   ║ ActiveCount ║ 
╠═════════════════════════╬═════════════╣ 
║ 2016-10-01 00:00:00.000 ║   0 ║ 
║ 2016-10-02 00:00:00.000 ║   3 ║ 
║ 2016-10-03 00:00:00.000 ║   3 ║ 
║ 2016-10-04 00:00:00.000 ║   3 ║ 
║ 2016-10-05 00:00:00.000 ║   2 ║ 
║ 2016-10-06 00:00:00.000 ║   2 ║ 
║ 2016-10-07 00:00:00.000 ║   2 ║ 
║ 2016-10-08 00:00:00.000 ║   3 ║ 
║ 2016-10-09 00:00:00.000 ║   4 ║ 
║ 2016-10-10 00:00:00.000 ║   4 ║ 
╚═════════════════════════╩═════════════╝ 
+1

優秀的解決方案! –

+0

@FelixPamittan - 哈謝謝;) –

+0

不錯的解決方案,但是如果學生2在11日再次不活動並且保持不活動,則該解決方案在11日之後的任何日期都不顯示正確的活動計數。一個簡單的'不EXISTS'檢查是檢查最大修改日期之前'dates'是使記錄不活躍像'NOT EXISTS(SELECT 1 FROM #student_status_history SH WHERE sh.student_id = s.student_id \t \t \t \t AND c.dates> = status_changed_dt HAVING MAX(CASE WHEN from_status = '活性' AND to_status = '無效' THEN status_changed_dt END) \t \t \t \t = MAX(status_changed_dt))' – ughai

0

我會做這樣的事情:

;with cte_dates as (
    select convert(date, '20161001', 112) as [date] 
    union all 
    select dateadd(day, 1, [date]) as [date] 
    from cte_dates as d 
    where 
     d.[date] < convert(date, '20161010', 112) 
) 
select 
    d.[date] as [Date], 
    sum(case when isnull(sh.to_status, s.[status]) = 'Active' then 1 else 0 end) as ActiveCount 
from cte_dates as d 
    left join #student as s on 
     s.created_dt <= d.[date] 
    outer apply (
     select top 1 sh.to_status 
     from #student_status_history as sh 
     where 
      sh.student_id = s.student_id and 
      sh.status_changed_dt <= d.[date] 
     order by 
      sh.status_changed_dt desc 
    ) as sh 
group by 
    d.[date]