Есть ли лучший способ рассчитать почасовую оплату труда и продаж? - PullRequest
0 голосов
/ 23 декабря 2018

У меня есть запрос, который в настоящее время работает, но основанный на нем неэффективен.В основном я пытаюсь сгруппировать данные по труду и продажам по часам.

И я хочу сделать это, задавая запрос в день.

Я использую PostgreSQL.

У меня есть куча записей о временных ударах, которые имеют employee_id, job_id и location_id, но если сотрудник включился и не закрылся, я должен проверить поле clock_out_time и установить*

Время планирования: 0.509 мс

Время выполнения: 0.498 мс

Я делаюэто с 30-50 записями, поэтому это не будет масштабироваться.

Что я могу сделать, чтобы улучшить это?

SELECT
  date_trunc('hour', tp.clock_in_time) AS hour,
  SUM(
    (
      EXTRACT (DAY FROM (CASE WHEN EXTRACT(YEAR FROM tp.clock_out_time) = -1 THEN now() ELSE tp.clock_out_time END - tp.clock_in_time))*24*60*60+
      EXTRACT (HOUR FROM (CASE WHEN EXTRACT(YEAR FROM tp.clock_out_time) = -1 THEN now() ELSE tp.clock_out_time END - tp.clock_in_time))*60*60+
      EXTRACT (MINUTE FROM (CASE WHEN EXTRACT(YEAR FROM tp.clock_out_time) = -1 THEN now() ELSE tp.clock_out_time END - tp.clock_in_time))*60+
      EXTRACT (SECOND FROM (CASE WHEN EXTRACT(YEAR FROM tp.clock_out_time) = -1 THEN now() ELSE tp.clock_out_time END - tp.clock_in_time))
    ) / 60 / 60.00 * (job.rate / 100.00)
  ) AS labor_costs,
  (
  SELECT 
    SUM(total) / 100.00
    FROM 
        ticket
    WHERE 
        open=false 
    AND 
        DATE_TRUNC('day', opened_at) = date_trunc('day', '2018-12-22T11:15:05-05:00'::date) 
    AND
      DATE_TRUNC('day', closed_at) = date_trunc('day', '2018-12-22T11:15:05-05:00'::date) 
    GROUP BY date_trunc('hour', opened_at) 
    ORDER BY date_trunc('hour', opened_at)
    ) AS hourly_sales
FROM 
  employee_time_punch as tp
INNER JOIN
  employee
ON 
  employee.id = tp.employee_id
INNER JOIN
  employee_job as job
ON
  job.id = tp.job_id
WHERE
  DATE_TRUNC('day', tp.clock_in_time) = DATE_TRUNC('day', '2006-01-02T11:15:05-05:00'::date)
AND
    DATE_TRUNC('day', CASE WHEN EXTRACT(YEAR FROM tp.clock_out_time) = -1 THEN now() ELSE tp.clock_out_time END) = DATE_TRUNC('day', '2006-01-02T11:15:05-05:00'::date)
GROUP BY 1
ORDER BY 1;

Ответы [ 2 ]

0 голосов
/ 23 декабря 2018

Как ответил @TheImpaler, способ сравнения дат должен быть улучшен, и вы можете использовать CTE для предварительного вычисления окна анализа.

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

  • выразить предложение WHERE как CROSS JOIN в окне анализа;COALESCE функция может быть использована по умолчанию от clock_in_time до NOW
  • для вычисления hourly_sales, используйте JOIN вместо подзапроса
  • используйте один EXTRACT(EPOCH FROM...) чтобы вычислить продолжительность смены сотрудника вместо повторения EXTRACT(HOUR/MINUTE/SECOND...)
  • перемещения фиксированных арифметических операций labor_costs вне функции SUM

Запрос:

WITH dates AS ( 
    SELECT 
        DATE_TRUNC('day', '2006-01-02T11:15:05-05:00'::date) AS wstart, 
        DATE_TRUNC('day', '2006-01-02T11:15:05-05:00'::date) + interval '1' day AS wend
)
SELECT
  date_trunc('hour', tp.clock_in_time) AS hour,
  SUM(
      EXTRACT(EPOCH FROM COALESCE(tp.clock_out_time, NOW()) - tp.clock_in_time) 
      * job.rate
   ) / 60 / 60 / 100.00 AS labor_costs,
  SUM(ticket.total)/100.00 AS hourly_sales
FROM 
    dates
    INNER JOIN employee_time_punch AS tp
        ON  tp.clock_in_time BETWEEN dates.wstart AND dates.wend
        AND COALESCE(tp.clock_out_time, NOW()) BETWEEN dates.wstart AND dates.wend
    INNER JOIN employee
        ON  employee.id = tp.employee_id
    INNER JOIN employee_job AS job
        ON  job.id = tp.job_id
    INNER JOIN ticket
        ON  ticket.open = false 
        AND ticket.opened_at BETWEEN dates.wstart AND dates.wend
        AND ticket.closed_at BETWEEN dates.wstart AND dates.wend
GROUP BY 1;

Для дополнительной оптимизации вы можете создать индексы для всех участвующих столбцов даты (один составной индекс на таблицу может работать хорошо):

  • в таблице employee_time_punch: clock_in_timeи clock_out_time
  • в таблице ticket: opened_at и closed_at
0 голосов
/ 23 декабря 2018

WHERE DATE_TRUNC ('day', tp.clock_in_time) = DATE_TRUNC ('day', '2006-01-02T11: 15: 05-05: 00' :: date)

Это единственное условие фильтрации вредит вашему запросу.Он страдает от синдрома «Выражение левой стороны в равенстве», который побеждает использование любого индекса.В этот момент PostgreSQL, вероятно, выполняет полное сканирование таблицы.

Вы можете сделать свой запрос намного быстрее, если перефразировать условие следующим образом:

WHERE tp.clock_in_time BETWEEN ...begin_of_day... AND ...end_of_day...

Вы можете предварительно вычислить эти значенияв CTE это то, что вам нужно.

И, конечно же, вам потребуется индекс для столбца, например:

create index ix1 on employee_time_punch (clock_in_time);

С этим изменением PostgreSQL будет выполнятьсявместо этого сканирование диапазона индекса, что-то намного быстрее.

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