Следующий запрос позволяет мне вычислить в часах (если меньше суток - конвертируется в часть дня) или днях (в зависимости от отсутствия длины) разницу между двумя датами:
В таблице отсутствий отсутствия могут быть записаны как с 00:00 до 23:59, с 08:30 до 16:30 (полный рабочий день), так и в течение нескольких часов в день, а иногда даже после рабочего дня, примеров может быть 13. :00 до 17:15. Я со своей стороны хочу рассчитать порцию только до 16:30.
Проблема, с которой я столкнулся в приведенном ниже запросе, заключается в том, что отсутствие длится более суток, а дата окончания является частью этого дня.
Рабочий день 8 часов (с 08:30 до 16:30).
Например, dteStartDate = 04.13.2024 08:30 и dteEndDAte 15.04.2024 10:45.
Запрос ниже дает мне 3 рабочих дня для примера, когда желаемый доход составляет 2,268.
Ниже я предоставил некоторые примеры данных о том, чего я ожидал:
SELECT
dteStartDateTime,
dteEndDateTime,
CASE
WHEN DATEDIFF(MINUTE, dteStartDateTime, dteEndDateTime) <= 1440 THEN
CAST(DATEDIFF(MINUTE,
CASE
WHEN CAST(dteStartDateTime AS time) < '08:30' THEN CAST(CAST(dteStartDateTime AS date) AS datetime) + CAST('08:30' AS datetime)
ELSE dteStartDateTime
END,
CASE
WHEN CAST(dteEndDateTime AS time) > '16:30' THEN CAST(CAST(dteEndDateTime AS date) AS datetime) + CAST('16:30' AS datetime)
ELSE dteEndDateTime
END
)/60.0 AS decimal(10,2))/8.0
ELSE
-- Adjusting for weekends in multi-day absences
(DATEDIFF(DAY, A.dteStartDateTime, A.dteEndDateTime) + 1)
- (DATEDIFF(WEEK, A.dteStartDateTime, A.dteEndDateTime) * 2)
END AS [Working Time Lost]
FROM TblCoverManagerAbsences A
JOIN TblCoverManagerAbsencesReasons AR ON A.intReason = AR.TblCoverManagerAbsencesReasonsId
OUTER APPLY
(
SELECT Min(txtStartDate) AS StartDate
FROM TblSchoolManagementTermDates
WHERE intSchoolYear = CASE
WHEN MONTH(getdate()) BETWEEN 9 AND 12 THEN YEAR(getdate())
ELSE YEAR(getdate()) - 1
END
) AS StartDate
WHERE
AR.txtName = 'Illness'
AND CONVERT(date, dteStartDateTime) >= CONVERT(date, StartDate.StartDate)
AND CONVERT(date, dteStartDateTime) <= DATEADD(week, DATEDIFF(week, 0, GETDATE()) - 1, 6)
Я не уверен, как я могу объединить это с наличием вторичной таблицы дат для поиска и проверки каждой даты в диапазоне дат.
Кроме того, T-SQL — это диалект SQL, который используется несколькими продуктами, включая Sybase, SQL Server и Azure Synapse. Какой продукт (R)DBMS вы здесь используете? Отредактируйте свой вопрос, отметив этот продукт, а также (если применимо) тег версии продукта.
Можем ли мы получить скрипт БД с примерами таблиц и данных?
Содержимое должно быть частью вопроса, @Developer , а не скрипкой (хотя рабочий пример может дополнять вопрос, она не должна требоваться для данных).
Данные я предоставил, но в виде скриншота из Excel. Этого достаточно?
Что значит dteEndDAte 15/04/2024 10:45
? Это 22:30? @Имран
«Я предоставил данные, но в виде скриншота из Excel. Этого достаточно?» Нет: Пожалуйста, не загружайте изображения кода/данных/ошибок, когда задаете вопрос.
@ArtBindu нет, сейчас 10:45 утра.
Ваше значение 2,268 неверно: вы рассчитали 2,15/8, потому что работа в последний день составила 2 часа 15 минут, тогда как должно быть 2,25/8: 15 минут составляют 1/4 часа.
@p3consulting — Извините, исправлено.
Могу ли я получить образцы данных с некоторыми данными, чтобы опробовать их и дать вам запрос. Примеры таблиц и вводные данные здесь dbfiddle.uk/ER_9PaV-
Что такое Working Time Lost
ценность? это hour/minute/seconds/days
? @Имран
Попробуйте использовать другую таблицу, содержащую всю информацию о датах, будь то рабочий или нерабочий день (праздник или выходные)... Это будет лучший способ разработать запрос... @Imran
Потерянное рабочее время – это количество потерянных часов, разделенное на общее количество часов, которые необходимо отработать (8 часов в рабочий день). Таким образом, результат конвертируется в часть рабочего дня, если отсутствие менее 8 часов.
@Developer — примеры данных приведены ниже: CREATE TABLE TblCoverManagerAbsences ( dteStartDateTime datetime NOT NULL, dteEndDateTime datetime NOT NULL, intReason int NOT NULL ); INSERT INTO TblCoverManagerAbsences (dteStartDateTime, dteEndDateTime, intReason) ЗНАЧЕНИЯ ('2024-02-27 08:30', '2024-02-27 16:30', 1), ('2024-03-11 08:30', ' 2024-03-11 16:30', 1), ('2024-03-13 08:30', '2024-03-15 10:45', 1), ('2023-11-13 11:05' , '2023-11-13 17:15', 1), ('2024-01-31 14:15', '2024-01-31 16:30', 1);
Таблица @Developer по причине: CREATE TABLE TblCoverManagerAbsencesReasons ( intReason int NOT NULL, txtName Varchar(50) ); INSERT INTO TblCoverManagerAbsencesReasons (intReason, txtName) ЗНАЧЕНИЯ (1, 'Болезнь')
Можете ли вы исправить таблицы и добавить, чтобы я мог проверить dbfiddle.uk/vCi8cAR9
@Developer — исправлено и обновлено: dbfiddle.uk/kmiVb7dS
Спасибо, что дали мне возможность изучить функцию SQL Server:
Решение в SQL Server:
with T as (
select *,
DATEDIFF(day, dtestartdatetime, dteenddatetime) AS dateDiff,
((DATEPART(HOUR, dtestartdatetime)*3600) + (DATEPART(MINUTE, dtestartdatetime)*60) + DATEPART(SECOND, dtestartdatetime)) as loginTime_sec,
((DATEPART(HOUR, dteenddatetime)*3600) + (DATEPART(MINUTE, dteenddatetime)*60) + DATEPART(SECOND, dteenddatetime)) as logoutTime_sec
from dailyLog
),
T0 as (
SELECT
cast('08:30:00' as TIME) as officialEntryTime,
cast('16:30:00' as TIME) as officialExitTime
),
T1 as (
SELECT
((DATEPART(HOUR, officialEntryTime)*3600) + (DATEPART(MINUTE, officialEntryTime)*60) + DATEPART(SECOND, officialEntryTime)) as officialLoginTime_sec,
((DATEPART(HOUR, officialExitTime)*3600) + (DATEPART(MINUTE, officialExitTime)*60) + DATEPART(SECOND, officialExitTime)) as officialLogoutTime_sec,
(((DATEPART(HOUR, officialExitTime)*3600) + (DATEPART(MINUTE, officialExitTime)*60) + DATEPART(SECOND, officialExitTime)) -
((DATEPART(HOUR, officialEntryTime)*3600) + (DATEPART(MINUTE, officialEntryTime)*60) + DATEPART(SECOND, officialEntryTime))) as officialTotalTime
From T0
),
T2 as (
select T.dtestartdatetime,
T.dteenddatetime,
ROUND(CAST(
CASE
WHEN(T.dateDiff = 0) THEN
(T.logoutTime_sec-T.loginTime_sec)
WHEN(T.dateDiff = 1) THEN
(T.logoutTime_sec-T1.officialLogoutTime_sec) +
(T1.officialLoginTime_sec-T.loginTime_sec)
WHEN(T.dateDiff > 1) THEN
(T.logoutTime_sec-T1.officialLogoutTime_sec) +
((T1.officialLogoutTime_sec-T1.officialLoginTime_sec) * (T.dateDiff - 1)) +
(T1.officialLoginTime_sec-T.loginTime_sec)
END AS REAL) / CAST(((dateDiff+1) * T1.officialTotalTime) AS REAL), 4) as Working_Time_Lost
from T, T1
)
select * from T2;
Пример вывода (SQL Server): db<>fiddle
Пример вывода (MySQL): db<>fiddle
Вывод:
Спасибо !!!
Это не работает для четвертой записи, в результате чего должно получиться 2,28, а не 0,0938. У меня есть решение, которое я прикреплю ниже.
Да, потому что в запросе предоставлена вся относительная информация. По вашим входным данным я создаю запрос. Этот запрос даст вам подход, как решить эту проблему. Итак, мой подход и ваши требования составляют идеальный запрос. Если ваша проблема решена, пожалуйста, примите ответ, закройте заявку
@Имран прочитай эту статью и загрузи свой вопрос: stackoverflow.com/help/how-to-ask
Следующее, кажется, решает мою проблему частичного отсутствия в последний день. Комментарий включен:
SELECT
dteStartDateTime,
dteEndDateTime,
CASE
WHEN DATEDIFF(MINUTE, dteStartDateTime, dteEndDateTime) <= 1440 THEN
CAST(DATEDIFF(MINUTE,
CASE
WHEN CAST(dteStartDateTime AS time) < '08:30' THEN CAST(CAST(dteStartDateTime AS date) AS datetime) + CAST('08:30' AS datetime)
ELSE dteStartDateTime
END,
CASE
WHEN CAST(dteEndDateTime AS time) > '16:30' THEN CAST(CAST(dteEndDateTime AS date) AS datetime) + CAST('16:30' AS datetime)
ELSE dteEndDateTime
END
)/60.0 AS decimal(10,2))/8.0
ELSE
-- Adjusting for weekends and partial days in multi-day absences
(DATEDIFF(DAY, dteStartDateTime, dteEndDateTime) + 1)
- (DATEDIFF(WEEK, dteStartDateTime, dteEndDateTime) * 2)
- (CASE
WHEN CAST(dteEndDateTime AS time) < '16:30' THEN 1 - CAST(DATEDIFF(MINUTE, CAST(CAST(dteEndDateTime AS date) AS datetime) + CAST('08:30' AS datetime), dteEndDateTime)/60.0 AS decimal(10,2))/8.0
ELSE 0
END)
END AS [Working Time Lost]
FROM TblCoverManagerAbsences A
JOIN TblCoverManagerAbsencesReasons AR ON A.intReason = AR.TblCoverManagerAbsencesReasonsId
JOIN TblStaff S ON A.txtTeacher = S.User_Code
OUTER APPLY
(
SELECT Min(txtStartDate) AS StartDate
FROM TblSchoolManagementTermDates
WHERE intSchoolYear = CASE
WHEN MONTH(getdate()) BETWEEN 9 AND 12 THEN YEAR(getdate())
ELSE YEAR(getdate()) - 1
END
) AS StartDate
WHERE
AR.txtName = 'Illness'
AND CONVERT(date, dteStartDateTime) >= CONVERT(date, StartDate.StartDate)
AND CONVERT(date, dteStartDateTime) <= DATEADD(week, DATEDIFF(week, 0, GETDATE()) - 1, 6)
Благодарим вас за вклад в сообщество Stack Overflow. Возможно, это правильный ответ, но было бы очень полезно предоставить дополнительные пояснения к вашему коду, чтобы разработчики могли понять ваши рассуждения. Это особенно полезно для новых разработчиков, которые не так хорошо знакомы с синтаксисом или пытаются понять концепции. Не могли бы вы отредактировать свой ответ, включив в него дополнительную информацию на благо сообщества?
Данные образцов расходных материалов и ожидаемые результаты помогут нам помочь вам в этом.