делать некоторые операции над столбцом даты в postgresql - PullRequest
0 голосов
/ 09 января 2019

У меня есть таблица со следующими столбцами:

staff_id INT, дата ДАТА, время ВРЕМЯ БЕЗ ВРЕМЕНИ ЗОНА.

Каждый день у каждого персонала есть вход и выход. Можно иметь более одного входа и выхода. Например, человек может прийти в 8:00 и выйти в 13:00, а затем снова вернуться в 16:36 и подождать, и, наконец, выйти в 19:20.

Таким образом, для каждой даты мне нужно вычислить сумму часов, в течение которых человек присутствовал на работе, и исходя из этого рабочего времени этого человека в каждом месяце. Поэтому мне нужно выбрать, который получает staff_id и возвращает рабочее время этого человека в каждом месяце. Например:

ID  1        2      3        4      5        6      7        8   9       10   11    12  
3   173.24   134    147.26   180    50.47    138    196.36   47  93.56   .56  78    139

1 Ответ

0 голосов
/ 09 января 2019

Сначала необходимо рассчитать продолжительность для каждой комбинации входа / выхода.

Предполагая, что у вас всегда есть четное количество записей для каждой даты для каждого staff_id, вы можете рассчитать продолжительность для каждой пары следующим образом:

select personnel_id, 
       "date", 
       case 
         when row_number() over w % 2 = 0 then "time" - lag("time") over w
       end as duration
from person_work
window w as (partition by personnel_id, "date" order by "time")

row_number() - это оконная функция , которая присваивает номер каждой строке. lag() - это еще одна оконная функция, которая получает значение столбца из предыдущей строки. Поскольку обе функции имеют одно и то же «определение окна», я объявил об этом только один раз с предложением window в конце. Выражение CASE вычисляет разницу столбца time для каждой второй строки. Строки входа имеют нечетный номер строки, строки выхода имеют четный номер строки. % 2 проверяет четность номеров строк.

На следующем шаге нам нужно объединить пары в длительности за месяц. Это можно сделать, опираясь на предыдущий запрос. Я использую общее табличное выражение для повторного использования предыдущего запроса:

with hours as (
  select personnel_id, 
         "date", 
         case 
           when row_number() over w % 2 = 0 then 
              -- this converts the interval into a decimal value
              extract(epoch from "time" - lag("time") over w)/3600
         end as hours
  from person_work
  window w as (partition by personnel_id, "date" order by "time")
), hours_per_month as (
  select personnel_id, 
         extract(year from "date")::int as work_year,
         extract(month from "date")::int as work_month,
         sum(hours) work_hours
  from hours
  where hours is not null
  group by personnel_id, work_year, work_month
)
select *
from hours_per_month;

extract(year from ...) возвращает год столбца date в виде десятичного значения. ::int - это тип приведенный , который просто преобразует его в целое число. Строго говоря, в этом нет необходимости.

extract(epoch from ..) возвращает длительность интервала в секундах. Разделив этот результат на 3600, вы получите интервал в часах.

Это вернет что-то вроде:

personnel_id | work_year | work_month | work_hours
-------------+-----------+------------+-----------
           1 |      2018 |          1 |      25.33
           1 |      2018 |          2 |      17.08
           1 |      2018 |          3 |       8.25

Затем на последнем шаге нам нужно превратить строки в столбцы. Это можно сделать с помощью условного агрегирования, используя предложение filter :

with hours as (
  select personnel_id, 
         "date", 
         case 
           when row_number() over w % 2 = 0 then extract(epoch from "time" - lag("time") over w)/3600
         end as hours
  from person_work
  window w as (partition by personnel_id, "date" order by "time")
), hours_per_month as (
  select personnel_id, 
         extract(year from "date")::int as work_year,
         extract(month from "date")::int as work_month,
         sum(hours) hours
  from hours
  where hours is not null
  group by personnel_id, work_year, work_month
)
select personnel_id, 
       work_year,
       sum(hours) filter (where work_month = 1) as hours_jan,
       sum(hours) filter (where work_month = 2) as hours_feb,
       sum(hours) filter (where work_month = 3) as hours_mar,
       sum(hours) filter (where work_month = 4) as hours_apr,
       sum(hours) filter (where work_month = 5) as hours_may,
       sum(hours) filter (where work_month = 6) as hours_jun,
       sum(hours) filter (where work_month = 7) as hours_Jul,
       sum(hours) filter (where work_month = 8) as hours_aug,
       sum(hours) filter (where work_month = 9) as hours_sep,
       sum(hours) filter (where work_month = 10) as hours_oct,
       sum(hours) filter (where work_month = 11) as hours_nov,
       sum(hours) filter (where work_month = 12) as hours_dec
from hours_per_month
group by personnel_id, work_year;

Это возвращает что-то вроде этого:

personnel_id | work_year | hours_jan | hours_feb | hours_mar | hours_apr | hours_may | hours_jun | hours_jul | hours_aug | hours_sep | hours_oct | hours_nov | hours_dec
-------------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+----------
           1 |      2018 |     25.33 |     17.08 |      8.25 |      ...  |    ...    |    ...    |    ...    |    ...    |     ....  |    ....   |     ...   |    ....  

Если вы просто хотите получить отчет за один год, вы можете использовать where work_year = ... в окончательном выборе и удалить столбец из списка выбора и group by

Онлайн пример: https://rextester.com/OEEAZ64654

...