我有一个事件表,其中包含状态更改的日期。我想要实现的是为每个日期生成摘要计数,但是我很挣扎,因为它不是按日期进行的直接计数,而是基于状态上次更改的计数。
数据如下:
------------------------------------------
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
--------------------------------------------------------
任何帮助都会感激不尽。
谢谢
答案 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