如果有人可以帮助解决以下问题。
我有一个表,其中我在第一列中有时间日期,在第二列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
我希望现在更容易理解...。
您发现的问题是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 ;
有几个项目需要注意: