Невозможно рассчитать рабочее время в диапазоне дат Postgresql - PullRequest
0 голосов
/ 17 апреля 2020

, если кто-то может помочь с вопросом ниже.

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

SELECT CAST(dnevnik_prijave.datum as date),ime_priimek, id_uporabnik,
    (SELECT min(datum) 
     from dnevnik_prijave 
     WHERE dnevnik_prijave.id_uporabnika=uporabniki.id_uporabnik 
     AND prijava_odjava='1' 
     AND datum>='2020-04-01' 
     AND datum<='2020-04-17'
     GROUP BY uporabniki.id_uporabnik) as prihod,        
    (SELECT max(datum) 
     FROM dnevnik_prijave 
     WHERE dnevnik_prijave.id_uporabnika=uporabniki.id_uporabnik 
     AND prijava_odjava='2' 
     AND datum>='2020-04-01' 
     AND datum<='2020-04-17') as odhod,      
    (SELECT extract(epoch from (odhod - prihod))/3600 as delovne_ure
     FROM (SELECT
                (SELECT min(datum) 
                 FROM dnevnik_prijave 
                 WHERE dnevnik_prijave.id_uporabnika=uporabniki.id_uporabnik 
                 AND prijava_odjava='1' 
                 AND datum>='2020-04-01' 
                 AND datum<='2020-04-17' 
                 GROUP BY uporabniki.id_uporabnik) as prihod,
                (SELECT max(datum) 
                 FROM dnevnik_prijave
                 WHERE dnevnik_prijave.id_uporabnika=uporabniki.id_uporabnik 
                 AND prijava_odjava='2' 
                 AND datum>='2020-04-01' 
                 AND datum<='2020-04-17') as odhod) as tabela1)
FROM uporabniki, dnevnik_prijave
WHERE dnevnik_prijave.id_uporabnika=uporabniki.id_uporabnik
AND dnevnik_prijave.datum >='2020-04-01' and dnevnik_prijave.datum<='2020-04-17'
GROUP BY (cast(dnevnik_prijave.datum as date)), uporabniki.id_uporabnik
ORDER BY (cast(dnevnik_prijave.datum as date)),ime_priimek asc

Пример таблицы dnevnik_prijave (которую я связываю с таблицей упорабники чтобы получить имена) ниже.

  id    username          datum               id_uporabnika prijava_odjava
  21424 worker 1    2020-04-17 11:47:06.119505      5000    1
  21422 worker 2    2020-04-17 10:52:24.291133      5001    1
  21426 worker 1    2020-04-17 13:53:57.757468      5000    2
  21425 worker 2    2020-04-17 13:35:40.584538      5001    2
  21424 worker 1    2020-04-17 14:01:06.119505      5000    1
  21422 worker 2    2020-04-17 15:52:24.291133      5001    1
  21426 worker 1    2020-04-17 17:53:57.757468      5000    2
  21425 worker 2    2020-04-17 17:35:40.584538      5001    2
  21424 worker 1    2020-04-18 11:47:06.119505      5000    1
  21422 worker 2    2020-04-18 10:52:24.291133      5001    1
  21426 worker 1    2020-04-18 13:53:57.757468      5000    2
  21425 worker 2    2020-04-18 13:35:40.584538      5001    2
  21424 worker 1    2020-04-18 14:01:06.119505      5000    1
  21422 worker 2    2020-04-18 15:52:24.291133      5001    1
  21426 worker 1    2020-04-18 17:53:57.757468      5000    2
  21425 worker 2    2020-04-18 17:35:40.584538      5001    2

Таблица, которую я хотел бы получить, выглядит следующим образом:

datum        ime_priime id_uporabnik    prihod           odhod           delovne
17.04.2020  LAZY WORKER 5000    2020-04-17 11:47:06 2020-04-17 17:53:57   6,2
17.04.2020  HARD WORKER 5001    2020-04-17 10:52:24 2020-04-17 17:35:40   6,6
18.04.2020  LAZY WORKER 5000    2020-04-18 11:47:06 2020-04-18 17:53:57   6,2
18.04.2020  HARD WORKER 5001    2020-04-18 10:52:24 2020-04-18 17:35:40   6,6

Надеюсь, теперь это более понятно ....

1 Ответ

0 голосов
/ 18 апреля 2020

Проблема, как вы обнаружили, состоит в том, что функции Min и Max возвращают значения в абсолютном выражении, но не имеют концептуальных промежуточных значений. Те, что вы можете Функция окна LEAD. Эта функция извлекает столбцы из следующей строки. В этом случае мы получаем объединение базовых значений из «1 строки» со следующей «2 строкой», в результате чего получается одна строка для каждого набора входов / выходов. Поскольку каждая метка времени, эти значения могут быть вычтены, чтобы получить Интервал между ними, а затем Суммируется для получения общего количества рабочих часов в день. Запрос ниже выполняет sh, что с пояснительными комментариями на каждом шаге (изнутри).

-- 5. finally since the window function used does not eliminate duplicate rows remove them now.
-- also convert the interval to a decimal number of hours to 1 decimal place. (ie .1hours)
select distinct on (wkday, id_uporabnika)
       to_char(wkday, 'yyyy-mm-dd') datum
     , username
     , id_uporabnika
     , datum     prihod
     , datum2    odhod
     , round( (extract('hours' from hrstotal)  + extract('minute' from hrstotal) /60)::numeric,1)
  from ( -- 4. now we have in hand an interval with actual time worked between for each '1 row' and corresponding '2 row'
         -- and intervals can cam be added, resulting in the actual hours for a single day  
         select wkday, username, id_uporabnika, datum, datum2
              , sum(wkhours) over( partition by id_uporabnika, wkday
                                       order by id_uporabnika, wkday
                                 ) hrstotal
           from ( -- 3, since datum from both rows (datum and datum2) are timestamps they can be subtracted directly
                  -- resulting in an interval. that interval being the time worked the between '2 row' and the '1 row'
                  -- and since the rows are now 'combined' discard the 2 row.  
                  select dp2.*, datum2-datum wkhours
                    from ( -- 2. pick up each '1 row' and combine the subsequent 2 row (lead function) by id_uporabnika and wkday
                           -- the result being the in time (datum from '1 row') and corresponding out time (datum from '2 row')
                           -- exist in the same row. 
                           select dp1.*
                                , lead(datum) over(partition by id_uporabnika, wkday 
                                                        order by id_uporabnika, datum
                                                  ) datum2 
                             from ( 
                                    -- 1. select the necessary columns and derive the date for reporting (wkday)                                
                                    select username, datum, id_uporabnika, prijava_odjava
                                         , date_trunc('day', datum) wkday
                                      from dnevnik_prijave
                                  ) dp1
                         ) dp2
                   where prijava_odjava =  1
                ) dp3
       ) db4 
 order by wkday, id_uporabnika ;

Есть пара элементов, на которые нужно обратить внимание:

  • Если временные метки для каждого id_uporabnika в шаблоне 1,2 .. 1,2 ..., будут получены интервал будет нулевым.
  • Данные для 1-й строки (prihod) и данные для последней 2-й строки (odhod) для каждого id_uporabnika содержатся в конечном результате. Однако общее рабочее время (деловое) не может быть получено из этих значений, поскольку они не учитывают нерабочее время между «2 строкой» и следующей «1 строкой».
  • Понятия не имею, как вы перешли от работника 1, работника 2 к работягу, ленивому работнику. Поэтому я даже не пытался.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...