У меня есть таблица с журналом изменений, каждая строка дает мне информацию о действиях.
Пример:
В другой таблице у меня есть информация о дате создания и закрытия билетов, и сейчас я просто делаю разницу между этими двумя датами. Но это не прозрачно для моей заинтересованной стороны.
Мне нужно точно подсчитать количество дней, когда тикет был открыт и когда он был закрыт из журнала изменений.
Я нашел похожие проблемы, подобные этой Разница дат MySQL между двумя строками а вот макс и мин но мне нужно считать каждое изменение
Вот я думаю, что для этого нужны все данные Стол
добавляю как выглядит таблица
Вы должны каким-то образом получить (логировать) строки, содержащие две метки времени, чтобы сделать некоторые различия так, как вы это делаете в настоящее время, на основе этой «другой таблицы».
Таким образом, базовой таблицей для запроса является ваша таблица журнала, предоставляющая метку времени, например. действия «открытия билета». Теперь присоединитесь к той же таблице во второй раз, чтобы получить временную метку действий «закрыть тикет». Критериями присоединения являются идентификатор тикета и отметка времени закрытия, которые должны быть минимальными, но позже открытия, а также правильный источник («открыто») и целевая статистика («закрыто»).
... в результате получается такой запрос (непроверенные и измененные имена столбцов для лучшего понимания, просто поймите идею):
SELECT ticket_id,timediff(l2_min_time,l1_time)
FROM ( SELECT l1.ticket_id,l1.time l1_time,min(l2.time) l2_min_time
FROM log_table l1
JOIN log_table l2
ON l1.ticket_id=l2.ticket_id
AND l1.time<=l2.time
AND l1.targetStatus = "open"
AND l2.targetStatus = "closed"
GROUP BY l1.ticket_id,l1.time
) t1
Имейте в виду, что вы работаете над перекрестным произведением «двух» потенциально больших таблиц, которые, вероятно, не оптимизированы для таких объединений. Этот запрос требует много ресурсов ЦП и/или ОЗУ и должен стать основой для хорошо настроенных индексов, прежде чем запускать его в производство.
Поскольку ваш образец changelog
данных начинается с status
изменения с Open
на Closed
, я предположил, что исходная дата и время Open
должна исходить из самого билета.
В своем вопросе вы указали:
Мне нужно точно подсчитать количество дней, когда тикет был открыт и когда он был закрыт из журнала изменений.
но number of days
между 2022-06-20 13:16:53
и 2022-06-20 13:27:12
, очевидно, равен 0, поэтому в этих примерах я сделал все расчеты в секундах.
Если вы делаете этот расчет только для билетов Closed
(поэтому для каждого статуса closed
/open
есть статус reopened
), то вы можете использовать простой GROUP BY
:
SELECT id,
SUM(
CASE
WHEN toString = 'closed' THEN TO_SECONDS(created)
WHEN toString IN ('open', 'reopened') THEN - TO_SECONDS(created)
END
) AS duration_sec
FROM (
SELECT created, toString, id FROM changelog WHERE field = 'status'
UNION ALL
SELECT created, 'Open', id FROM tickets
) t
GROUP BY id;
Другой подход заключается в использовании коррелированного подзапроса для получения времени closed
, а если заявка не закрыта, используйте текущую дату и время, чтобы рассчитать, как долго она была открыта:
SELECT id, SUM(TIMESTAMPDIFF(SECOND, opened, IFNULL(closed, NOW()))) AS duration_sec
FROM (
SELECT id, created AS opened, (SELECT created FROM changelog WHERE id = t.id AND field = 'status' AND toString = 'closed' AND created > t.created ORDER BY created ASC LIMIT 1) AS closed
FROM tickets t
UNION ALL
SELECT id, created AS opened, (SELECT created FROM changelog WHERE id = cl.id AND field = 'status' AND toString = 'closed' AND created > cl.created ORDER BY created ASC LIMIT 1) AS closed
FROM changelog cl
WHERE cl.field = 'status' AND toString IN ('reopened')
) t
GROUP BY id;
Другой подход, использующий оконную функцию LEAD() для получения значения created
из следующей строки и Общие табличные выражения (CTE) для разделения запроса:
WITH ticket_status_changes AS (
SELECT created, toString, id FROM changelog WHERE field = 'status'
UNION ALL
SELECT created, 'open', id FROM tickets
),
ticket_open_close AS (
SELECT
id, toString, created AS opened,
IF(
toString IN ('open', 'reopened'),
LEAD(created) OVER (PARTITION BY id ORDER BY created ASC),
NULL
) AS closed
FROM ticket_status_changes
)
SELECT id, SUM(TIMESTAMPDIFF(SECOND, opened, IFNULL(closed, NOW()))) AS duration_sec
FROM ticket_open_close
WHERE toString IN ('open', 'reopened')
GROUP BY id;
вы можете добавить свою структуру таблицы с примерами данных, чтобы мы могли помочь вам с запросом :)