Получите общее количество последовательных значений - PullRequest
0 голосов
/ 11 марта 2020

У меня есть таблица, в которой хранятся часы, сообщенные пользователями для выполненных действий, по отчетным годам и месяцам. У меня есть функция в старой базе данных Oracle, которую мне нужно реплицировать на сервере SQL, которая показывает текущее значение для последовательных отчетов за 0 часов. то есть, когда кто-то не сообщил часов в течение 3 месяцев подряд, число будет равно 3, если они сообщили за определенный месяц, счетчик затем будет сброшен до 0, чтобы подсчитывать только последовательные отчеты с 0 ч.

Таблица всегда предварительно заполнена ожидаемым количеством отчетных периодов для пользователя. то есть, если пользователь должен сообщать о часах в течение 6 месяцев, мы создадим 6 строк в этой таблице с NULL в поле HOURS_REPORTED .

Вот образец PL SQL.

FUNCTION FN_NIL_RPT_CNT ( PI_USER_ID IN NUMBER )
 RETURN NUMBER
 IS

 LV_NIL_CNT NUMBER := 0;

 CURSOR CR_DTLS IS
 SELECT YEAR
       ,MONTH
       ,HOURS_REPORTED
 FROM HOURS
 WHERE STATUS = 'CURRENT'
       AND USER_ID = PI_USER_ID
 ORDER BY TO_DATE(MONTH||YEAR, 'mmyyyy'); 

 BEGIN
   FOR LR_HR IN CR_DTLS
   LOOP
     EXIT WHEN TO_DATE(LR_HR.MONTH||LR_HR.YEAR,'mmyyyy') >= TRUNC(SYSDATE,'MM');

     IF LR_HR.HOURS_REPORTED IS NOT NULL
     THEN
       IF LR_HR.HOURS_REPORTED = 0
       THEN
         LV_NIL_CNT := LV_NIL_CNT + 1;
       ELSE
         LV_NIL_CNT := 0;
       END IF;
     ELSE 
       NULL; -- ignore
     END IF;
   END LOOP;
 END;
 RETURN LV_NIL_CNT;
END FN_NIL_RPT_CNT;

Таким образом, если бы курсор содержал следующее, ожидаемый результат был бы равен 2, поскольку в 2019/10 году было сообщено о часах, которые затем сбросили бы счетчик на 0, а в последующие 2 месяца было 0 часы работы.

1 Ответ

2 голосов
/ 11 марта 2020

Мое предложение состоит в том, чтобы использовать прямую sql и потерять логи курсора c. В любое время это будет более эффективно, быстрее и дешевле

Например: следующий запрос может дать вам last_consecutive_count

CREATE TABLE dbo.t
(
    USER_ID INT
   ,YEAR INT
   ,MONTH INT
   ,HOURS_REPORTED INT
);

insert into dbo.t
VALUES
( 254, '2017', '12', 0 ), 
( 254, '2018', '01', 8 ), 
( 254, '2018', '02', 11 ), 
( 254, '2018', '03', 16 ), 
( 254, '2018', '04', 12 ), 
( 254, '2018', '05', 16 ), 
( 254, '2018', '06', 20 ), 
( 254, '2018', '07', 14 ), 
( 254, '2018', '08', 12 ), 
( 254, '2018', '09', 11 ), 
( 254, '2018', '10', 22 ), 
( 254, '2018', '11', 10 ), 
( 254, '2018', '12', 6 ), 
( 254, '2019', '01', 11 ), 
( 254, '2019', '02', 12 ), 
( 254, '2019', '03', 5 ), 
( 254, '2019', '04', 12 ), 
( 254, '2019', '05', 10 ), 
( 254, '2019', '06', 9 ), 
( 254, '2019', '07', 10 ), 
( 254, '2019', '08', 9 ), 
( 254, '2019', '09', 0 ), 
( 254, '2019', '10', 4 ), 
( 254, '2019', '11', 8 ), 
( 254, '2019', '12', 0 ), 
( 254, '2020', '01', 0 ), 
( 254, '2020', '02', 5 ),
( 254, '2020', '03', 7 )



WITH data
AS (SELECT *
          ,CAST(CONCAT(YEAR, '-', MONTH, '-01') AS DATE) AS dt
          ,ROW_NUMBER() OVER (ORDER BY YEAR desc, MONTH desc) AS rnk
          ,ROW_NUMBER() OVER (partition by case when hours_reported=0 then 1 end ORDER BY YEAR desc, MONTH desc) AS rnk2
          ,DATEADD(MONTH, -ROW_NUMBER() OVER (ORDER BY YEAR, MONTH), CAST(CONCAT(YEAR, '-', MONTH, '-01') AS DATE)) AS grp
    FROM dbo.t
    WHERE USER_ID = 254 /* you can parameterize this in your sql server function*/
    )
   ,last_val
    as(SELECT *
          ,case when hours_reported<>0 then rnk else rnk-rnk2 end as val1
          ,rank() over(order by case when hours_reported<>0 then rnk else rnk-rnk2 end) as rnk_min
      FROM data
     )
select count(*) as last_consec_cnt
  from last_val
 where rnk_min=1


+----------------------+
| last_consecutive_cnt |
+----------------------+
|                    1 |
+----------------------+

Вы можете поместить это в функцию и передать user_id таблица ..

Вот ссылка на дб скрипку. https://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=9ac60e62a99e2465cc8e979ae9f29f7c

...