Можно ли создать счет по дате в таблице исторических событий? - PullRequest
2 голосов
/ 17 мая 2019

У меня есть таблица событий, которая содержит дату изменения статуса. Я пытаюсь добиться сводных подсчетов для каждой даты, однако я изо всех сил, потому что это не прямой подсчет по дате, а подсчет, основанный на последнем изменении статуса.

Данные следующие:

------------------------------------------
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.199 никаких изменений в статусе не произошло, поэтому он показывает то же самое, что и 05.05.199:

---------------------------------------------------------
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 голосов
/ 17 мая 2019

Вы можете сделать это с помощью оконных функций агрегирования:

Установка Oracle :

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;

Запрос

SELECT DISTINCT
       dt AS "TIMESTAMP",
       COUNT( CASE new_status WHEN 1 THEN IT_ID END ) OVER ( ORDER BY dt RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW )
         - COUNT( CASE old_status WHEN 1 THEN IT_ID END ) OVER ( ORDER BY dt RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW )
         AS Status1,
       COUNT( CASE new_status WHEN 2 THEN IT_ID END ) OVER ( ORDER BY dt RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW )
         - COUNT( CASE old_status WHEN 2 THEN IT_ID END ) OVER ( ORDER BY dt RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW )
         AS Status2,
       COUNT( CASE new_status WHEN 3 THEN IT_ID END ) OVER ( ORDER BY dt RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW )
         - COUNT( CASE old_status WHEN 3 THEN IT_ID END ) OVER ( ORDER BY dt RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW )
         AS Status3,
       COUNT( CASE new_status WHEN 4 THEN IT_ID END ) OVER ( ORDER BY dt RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW )
         - COUNT( CASE old_status WHEN 4 THEN IT_ID END ) OVER ( ORDER BY dt RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW )
         AS Status4
FROM   test_data t
       RIGHT OUTER JOIN (
         SELECT min_dt + LEVEL - 1 AS dt
         FROM   ( SELECT MIN("TIMESTAMP") AS min_dt,
                         MAX("TIMESTAMP") AS max_dt
                  FROM   test_data 
                )
         CONNECT BY min_dt + LEVEL - 1 <= max_dt
       ) c
       ON ( c.dt = t."TIMESTAMP" )
ORDER BY "TIMESTAMP" DESC

выход

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

дБ <> скрипка здесь

0 голосов
/ 17 мая 2019

Я думаю об решении этой проблемы, получая статус каждого человека на каждой дате. Для этого требуется 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;

Здесь - это дБ <> скрипка.

0 голосов
/ 17 мая 2019

Вы можете использовать функцию поворота SQL.У меня нет оракула DB для проверки этого:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...