无法计算日期范围内的工作时间Postgresql

问题描述 投票:0回答:1

如果有人可以帮助解决以下问题。

我有一个表,其中我在第一列中有时间日期,在第二列prijava_odjava中,我有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(我与表uporabniki链接以获得名称)在下面。

  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

我希望现在更容易理解...。

postgresql
1个回答
0
投票

您发现的问题是Min和Max函数以绝对术语返回值,但没有概念中间值。那些你可以Window Function铅。此函数从下一行提取列。在这种情况下,我们将“ 1行”中的数据值与随后的“ 2行”中的数据相结合,从而得到每个输入/输出集的一行。由于每个都是时间戳,因此可以将这些值相减以在它们之间产生一个Interval,然后相加以得出每天的总工作时间。下面的查询通过在每个步骤(从内到外)的解释性注释来实现这一点。

-- 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 ;

有几个项目需要注意:

  • 如果在1,2 .. 1,2 ...模式中每个id_uporabnika的时间戳,则结果间隔将为null。
  • 每个id_uporabnika的第一个“ 1行”(基准)的基准和最后一个“ 2行”(odhod)的基准包含在最终结果中。但是,无法从这些值中得出总的工作时间(偏差),因为它们没有考虑“ 2行”和随后的“ 1行”之间的非工作时间。
  • 我不知道您是如何从工人1,工人2变成勤奋工人,懒惰工人的。所以我什至没有尝试。
© www.soinside.com 2019 - 2024. All rights reserved.