是否可以在历史事件表上按日期创建计数?

时间:2019-05-17 10:00:10

标签: sql oracle

我有一个事件表,其中包含状态更改的日期。我想要实现的是为每个日期生成摘要计数,但是我很挣扎,因为它不是按日期进行的直接计数,而是基于状态上次更改的计数。

数据如下:

------------------------------------------
IT_ID   NEW_STATUS  OLD_STATUS  TIMESTAMP
------------------------------------------
100     4           3           06/05/2019
100     3           2           04/05/2019
200     2           1           03/05/2019
100     2           1           02/05/2019
300     2           1           02/05/2019
200     1           -           01/05/2019
100     1           -           01/05/2019
300     1           -           01/05/2019
-------------------------------------------

我已经尝试了分组,但是由于上面的原因,下面的SQL不能直接计算,因此无法正常工作。

select max(trunc(timestamp)), new_status ,count(new_status)
from status_hist
where trunc(timestamp) >= '01/01/2019'
group by trunc(timestamp), new_status

理想情况下,我希望采用以下格式的数据,但是关键是要根据每个日期进行计数。请注意,由于在05/05/19上没有发生任何状态更改,因此它与19/05/04上的状态相同:

---------------------------------------------------------
Date        Status 1    Status 2    Status 3    Status 4
---------------------------------------------------------
06/05/2019  0           2           0           1
05/05/2019  0           2           1           0
04/05/2019  0           2           1           0
03/05/2019  0           3           0           0
02/05/2019  1           2           0           0
01/05/2019  3           0           0           0
--------------------------------------------------------

任何帮助都会感激不尽。

谢谢

3 个答案:

答案 0 :(得分:0)

您可以使用窗口聚合功能来做到这一点:

Oracle设置

$[<scope-modifier>:]<name> = <value>

查询

CREATE TABLE test_data ( IT_ID, NEW_STATUS, OLD_STATUS, "TIMESTAMP" ) AS
SELECT 100, 4, 3,    DATE '2019-05-06' FROM DUAL UNION ALL
SELECT 100, 3, 2,    DATE '2019-05-04' FROM DUAL UNION ALL
SELECT 200, 2, 1,    DATE '2019-05-03' FROM DUAL UNION ALL
SELECT 100, 2, 1,    DATE '2019-05-02' FROM DUAL UNION ALL
SELECT 300, 2, 1,    DATE '2019-05-02' FROM DUAL UNION ALL
SELECT 200, 1, NULL, DATE '2019-05-01' FROM DUAL UNION ALL
SELECT 100, 1, NULL, DATE '2019-05-01' FROM DUAL UNION ALL
SELECT 300, 1, NULL, DATE '2019-05-01' FROM DUAL;

输出

TIMESTAMP | STATUS1 | STATUS2 | STATUS3 | STATUS4
:-------- | ------: | ------: | ------: | ------:
06-MAY-19 |       0 |       2 |       0 |       1
05-MAY-19 |       0 |       2 |       1 |       0
04-MAY-19 |       0 |       2 |       1 |       0
03-MAY-19 |       0 |       3 |       0 |       0
02-MAY-19 |       1 |       2 |       0 |       0
01-MAY-19 |       3 |       0 |       0 |       0

db <>提琴here

答案 1 :(得分:0)

我想通过获取每个日期的每个人的状态来处理此问题。这需要cross join来获得人员/日期组合,然后进行一些汇总:

WITH dates as (
      SELECT min_dt + LEVEL - 1 AS dt
      FROM (SELECT MIN(ts) AS min_dt, MAX(ts) AS max_dt
            FROM   test_data 
           )
      CONNECT BY min_dt + LEVEL - 1 <= max_dt
     )
SELECT d.dt, i.it_id, max(td.new_status) keep (dense_rank first order by td.ts desc) as status
FROM dates d CROSS JOIN
     (SELECT DISTINCT IT_ID FROM test_data) i LEFT JOIN
     test_data td
     ON td.IT_ID = i.IT_ID AND td.ts <= d.dt
GROUP BY d.dt, i.it_id;

dates CTE只是在计算所有日期。其余的都处于最新状态。

然后可以扩展它以汇总(或透视)结果:

WITH dates as (
      SELECT min_dt + LEVEL - 1 AS dt
      FROM (SELECT MIN(ts) AS min_dt, MAX(ts) AS max_dt
            FROM   test_data 
           )
      CONNECT BY min_dt + LEVEL - 1 <= max_dt
     ),
     di as (
      SELECT d.dt, i.it_id, max(td.new_status) keep (dense_rank first order by td.ts desc) as status
      FROM dates d CROSS JOIN
           (SELECT DISTINCT IT_ID FROM test_data) i LEFT JOIN
           test_data td
           ON td.IT_ID = i.IT_ID AND td.ts <= d.dt
      GROUP BY d.dt, i.it_id
     )
select dt,
       sum(case when status = 1 then 1 else 0 end) as num_1,
       sum(case when status = 2 then 1 else 0 end) as num_2,
       sum(case when status = 3 then 1 else 0 end) as num_3,
       sum(case when status = 4 then 1 else 0 end) as num_4
from di
group by dt
order by dt desc;

Here是db <>小提琴。

答案 2 :(得分:-1)

您可以使用SQL的数据透视功能。 我没有Oracle数据库可以对此进行测试:

declare @dates table(Date timestamp(3), NEW_STATUS number(10))

 v_StartDate DATE := (SELECT MIN(timestamp) FROM [test].dbo)
 v_EndDate DATE := (SELECT MAX(timestamp) FROM [test].dbo)

insert into @dates 
SELECT  nbr * INTERVAL '1' DAY(5) - 1 + v_StartDate as 'Date', null as NEW_STATUS
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY c.object_id ) AS Nbr
FROM      sys.columns c
) nbrs
WHERE   nbr - 1 <= v_EndDate - v_StartDate


SELECT timestamp as 'Date', 1 AS 'Status 1', 2 AS 'Status 2', 3 AS 'Status 3', 4 AS 'Status 4'  
FROM   
(SELECT Date as 'timestamp', NVL(NVL(d.new_status, t.NEW_STATUS),t2.NEW_STATUS) as new_status
FROM @dates d
left outer join Table_test t on d.Date = t.TIMESTAMP
left outer join Table_test t2 on INTERVAL '-1' DAY(5) +d.Date = t2.TIMESTAMP and NVL(d.new_status, t.NEW_STATUS) is null ) p  
PIVOT  
(  
COUNT (new_status)  
FOR new_status IN  
( 1, 2, 3, 4 )  
) AS pvt  
ORDER BY pvt.TIMESTAMP desc

我的Microsoft SQL语法是:

declare @dates table([Date] datetime, [NEW_STATUS] int)

DECLARE @StartDate DATE = (SELECT MIN(timestamp) FROM [test].[dbo].[Table_test])
DECLARE @EndDate DATE = (SELECT MAX(timestamp) FROM [test].[dbo].[Table_test])

insert into @dates 
SELECT  DATEADD(DAY, nbr - 1, @StartDate) as 'Date', null as NEW_STATUS
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY c.object_id ) AS Nbr
FROM      sys.columns c
) nbrs
WHERE   nbr - 1 <= DATEDIFF(DAY, @StartDate, @EndDate)


SELECT timestamp as 'Date', [1] AS 'Status 1', [2] AS 'Status 2', [3] AS 'Status 3', [4] AS 'Status 4'  
FROM   
(SELECT Date as 'timestamp', ISNULL(ISNULL(d.new_status, t.NEW_STATUS),t2.NEW_STATUS) as new_status
FROM @dates d
left outer join Table_test t on d.Date = t.TIMESTAMP
left outer join Table_test t2 on DATEADD(DAY,-1,d.Date) = t2.TIMESTAMP and ISNULL(d.new_status, t.NEW_STATUS) is null ) p  
PIVOT  
(  
COUNT (new_status)  
FOR new_status IN  
( [1], [2], [3], [4] )  
) AS pvt  
ORDER BY pvt.TIMESTAMP desc