SQL рассчитывать без рабочих дней - PullRequest
2 голосов
/ 03 октября 2019

Мне нужно написать SQL-запрос оракула, чтобы вычислить общее количество «дней без заданий» для данного месяца и идентификатор задания из таблицы LOG. В таблице есть такие данные:

LogId JobID       LogDate     JobStatu
1       1         09/01/2019  active
2       1         09/02/2019  end
3       2         08/03/2019  active
4       2         08/05/2019  suspended
5       2         08/08/2019  active
6       2         08/15/2019  end

рабочие дни - дни между активным и конечным ИЛИ активным и приостановленным. Работа может быть приостановлена ​​между. Таким образом, в вышеприведенном примере для рабочего дня месяца сентябрь равен 1 (начался 1-го числа и закончился 2-го числа), а для месяца августа - 9 дней (начался 3-го числа и приостановлен 5-го, поэтому всего 2 дня работы и снова начался8-е и окончание 15-е - 7 дней, поэтому общее количество рабочих дней 2 + 7 = 9).

ожидаемый результат, дни без работы - 29 сентября (общее количество дней в месяце 30 - 1 деньработы) и за август 22 (31 - 9).

Может кто-нибудь, пожалуйста, помогите мне написать запрос Oracle SQL для этого.

Спасибо.

Ответы [ 4 ]

2 голосов
/ 03 октября 2019

Вы можете попробовать SQL для сопоставления с образцом (MATCH_RECOGNIZE):

WITH t(LogId, JobID, LogDate, JobStatus) AS
    (SELECT 1, 1, TO_DATE('09/01/2019','mm/dd/YYYY'), 'active' FROM dual UNION ALL
    SELECT 2, 1, TO_DATE('09/02/2019','mm/dd/YYYY'), 'end' FROM dual UNION ALL
    SELECT 3, 2, TO_DATE('08/03/2019','mm/dd/YYYY'), 'active' FROM dual UNION ALL
    SELECT 4, 2, TO_DATE('08/05/2019','mm/dd/YYYY'), 'suspended' FROM dual UNION ALL
    SELECT 5, 2, TO_DATE('08/08/2019','mm/dd/YYYY'), 'active' FROM dual UNION ALL
    SELECT 6, 2, TO_DATE('08/15/2019','mm/dd/YYYY'), 'end' FROM dual UNION ALL
    SELECT 7, 3, TO_DATE('06/01/2019','mm/dd/YYYY'), 'active' FROM dual UNION ALL
    SELECT 8, 3, TO_DATE('06/04/2019','mm/dd/YYYY'), 'suspended' FROM dual UNION ALL
    SELECT 9, 3, TO_DATE('06/08/2019','mm/dd/YYYY'), 'active' FROM dual UNION ALL
    SELECT 10, 3, TO_DATE('06/12/2019','mm/dd/YYYY'), 'suspended' FROM dual UNION ALL
    SELECT 11, 3, TO_DATE('06/18/2019','mm/dd/YYYY'), 'active' FROM dual UNION ALL
    SELECT 12, 3, TO_DATE('06/25/2019','mm/dd/YYYY'), 'end' FROM dual),
m as 
    (SELECT JobID, ACTIVE_DATE, END_DATE, SUSPEND_DAYS, JobStatus, var_match
    FROM t
         MATCH_RECOGNIZE (
              PARTITION BY JobID
              ORDER BY LogId
              MEASURES 
                    CLASSIFIER() AS var_match,
                    FINAL FIRST(s_ACTIVE.LogDate) AS ACTIVE_DATE,
                    FINAL LAST(s_END.LogDate) AS END_DATE,
                    (s_REACTIVE.LogDate - s_SUSPENDED.LogDate) AS SUSPEND_DAYS
              ALL ROWS PER MATCH
              PATTERN ( s_ACTIVE (s_SUSPENDED s_REACTIVE)* s_END )
              DEFINE
                    s_ACTIVE AS JobStatus = 'active',
                    s_REACTIVE AS JobStatus = 'active',
                    s_END AS JobStatus = 'end',
                    s_SUSPENDED AS JobStatus = 'suspended' 
         )
    )
SELECT JobID, 
    MIN(ACTIVE_DATE) AS START_DATE, 
    MAX(END_DATE) AS END_DATE, 
    SUM(SUSPEND_DAYS) AS SUSPENDED_DAYS,
    MAX(END_DATE) - MIN(ACTIVE_DATE) - NVL(SUM(SUSPEND_DAYS),0) AS ACTIVE_DAYS,
    EXTRACT(DAY FROM (LAST_DAY(MIN(ACTIVE_DATE)))) - (MAX(END_DATE) - MIN(ACTIVE_DATE) - NVL(SUM(SUSPEND_DAYS),0)) AS ACTIVE_DAYS_FROM_CURRENT_MONTH
FROM m    
WHERE JobStatus = 'active'
GROUP BY JobID
ORDER BY JobID; 

Результат:

+-------------------------------------------------------------------------------------+
|JOBID|START_DATE|END_DATE  |SUSPENDED_DAYS|ACTIVE_DAYS|ACTIVE_DAYS_FROM_CURRENT_MONTH|
+-------------------------------------------------------------------------------------+
|1    |01.09.2019|02.09.2019|              |1          |29                            |
|2    |03.08.2019|15.08.2019|3             |9          |22                            |
|3    |01.06.2019|25.06.2019|10            |14         |16                            |
+-------------------------------------------------------------------------------------+
1 голос
/ 03 октября 2019

Вы можете использовать этот код в Oracle.

В WITH AS я предоставил как ваши тестовые данные (log_table), так и таблицу дат (dates).

Вы можете сохранить деталь с таблицей дат, если в вашей базе данных ее еще нет:

with log_table (LogId, JobID, LogDate, JobStatu) as (
    select 1, 1, to_date('09/01/2019', 'MM/DD/YYYY'), 'active' from dual
    union all select 2, 1, to_date('09/02/2019', 'MM/DD/YYYY'), 'end' from dual
    union all select 3, 2, to_date('08/03/2019', 'MM/DD/YYYY'), 'active' from dual
    union all select 4, 2, to_date('08/05/2019', 'MM/DD/YYYY'), 'suspended' from dual
    union all select 5, 2, to_date('08/08/2019', 'MM/DD/YYYY'), 'active' from dual
    union all select 6, 2, to_date('08/15/2019', 'MM/DD/YYYY'), 'end' from dual
),
dates as (
    select trunc(to_date('31.12.2019', 'DD.MM.YYYY') - (rownum - 1)) as date_val
         , extract(month from trunc (to_date('31.12.2019', 'DD.MM.YYYY') - (rownum - 1))) as month_val
      from dual connect by rownum < 366
)
select dts.month_val as "Month"
     , count(dts.date_val) as "Number of days"
  from dates dts
 where not exists (select 1
                     from log_table lt1
                    where lt1.JobStatu = 'active'
                      and dts.date_val between lt1.LogDate 
                                           and (select min(LogDate) - 1
                                                  from log_table
                                                 where JobID = lt1.JobID
                                                   and JobStatu in ('end', 'suspended')
                                                   and LogDate > lt1.LogDate)
                      and rownum = 1)
 group by dts.month_val
 order by dts.month_val asc 

Результат:

    +--------+----------------+
    | Month  | Number of days | 
    +--------+----------------+
    |     1  |             31 | 
    |     2  |             28 | 
    |     3  |             31 | 
    |     4  |             30 | 
    |     5  |             31 | 
    |     6  |             30 | 
    |     7  |             31 | 
    |     8  |             22 | 
    |     9  |             29 | 
    |    10  |             31 | 
    |    11  |             30 | 
    |    12  |             31 | 
    +--------+----------------+
1 голос
/ 03 октября 2019

Вы можете попробовать ниже - просто измените datediff() на эквивалентную функцию оракула

DEMO

select jobid,SUM(logdate-t) as totaldays from 
(
select t1.*,lag(logdate) over(partition by jobid order by logid) as t,
lag(jobstatus) over(partition by jobid order by logid) as st
from t1
)A where st='active' or (jobstatus='active' and (st is not null and st<>'suspended'))
group by jobid

ПРИМЕЧАНИЕ: эта демонстрационная версия находится на сервере sql, но в вашем случае это работает, вам нужно изменить часть datediff ()

0 голосов
/ 03 октября 2019

Я пытаюсь преобразовать ответ @Fahmi в Oracle, но получаю ошибку:

 with Log_data as
( 
select tkt_logtest_kb.*, lag(logdate) over(partition by jobid order by logid) as t,
lag(jobstatus) over(partition by jobid order by logid) as st
from tkt_logtest_kb) 
select Log_data.jobid, sum(to_date(t, 'MM-dd-yyyy') - to_date(logdate, 'MM-dd-yyyy')) as totaldays from  Log_data 
where st='Active' or (jobstatus='Active' and (st is not null and st<>'Suspended'))
group by Log_data.jobid

ORA-01858: был обнаружен нецифровый символ, где ожидалось числовое значение 01858. 00000 - "нечисловой символ был найден там, где ожидалось числовое значение "* Причина: входные данные для преобразования с использованием модели формата даты были неправильными. Входные данные не содержали число, для которого номер требовался моделью формата. * Действие: Исправьте входные данные или модель формата даты, чтобы убедиться, что элементы совпадают по количеству и типу. Затем повторите операцию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...