Расчет отсутствия по часам и дням между датами

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

В таблице отсутствий отсутствия могут быть записаны как с 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.

Ниже я предоставил некоторые примеры данных о том, чего я ожидал:

dteStartDateTime dteEndDateTime Потерянное рабочее время 27.02.2024 08:30 27.02.2024 16:30 1 03.11.2024 08:30 03.11.2024 16:30 1 03.12.2024 08:30 03.12.2024 16:30 1 03.13.2024 08:30 03.15.2024 10:45 2,28125 11.13.2023 11:05 11.13.2023 17:15 0,6775 01.01.2024 14:15 01.31.2024 16:30 0,28125
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)

Я не уверен, как я могу объединить это с наличием вторичной таблицы дат для поиска и проверки каждой даты в диапазоне дат.

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

Thom A 29.04.2024 13:38

Кроме того, T-SQL — это диалект SQL, который используется несколькими продуктами, включая Sybase, SQL Server и Azure Synapse. Какой продукт (R)DBMS вы здесь используете? Отредактируйте свой вопрос, отметив этот продукт, а также (если применимо) тег версии продукта.

Thom A 29.04.2024 13:39

Можем ли мы получить скрипт БД с примерами таблиц и данных?

Developer 29.04.2024 13:49

Содержимое должно быть частью вопроса, @Developer , а не скрипкой (хотя рабочий пример может дополнять вопрос, она не должна требоваться для данных).

Thom A 29.04.2024 13:56

Данные я предоставил, но в виде скриншота из Excel. Этого достаточно?

Imran 29.04.2024 14:02

Что значит dteEndDAte 15/04/2024 10:45 ? Это 22:30? @Имран

Art Bindu 29.04.2024 14:29

«Я предоставил данные, но в виде скриншота из Excel. Этого достаточно?» Нет: Пожалуйста, не загружайте изображения кода/данных/ошибок, когда задаете вопрос.

Thom A 29.04.2024 14:39

@ArtBindu нет, сейчас 10:45 утра.

Imran 29.04.2024 15:10

Ваше значение 2,268 неверно: вы рассчитали 2,15/8, потому что работа в последний день составила 2 часа 15 минут, тогда как должно быть 2,25/8: 15 минут составляют 1/4 часа.

p3consulting 29.04.2024 15:11

@p3consulting — Извините, исправлено.

Imran 29.04.2024 15:19

Могу ли я получить образцы данных с некоторыми данными, чтобы опробовать их и дать вам запрос. Примеры таблиц и вводные данные здесь dbfiddle.uk/ER_9PaV-

Developer 29.04.2024 15:24

Что такое Working Time Lostценность? это hour/minute/seconds/days? @Имран

Art Bindu 29.04.2024 16:21

Попробуйте использовать другую таблицу, содержащую всю информацию о датах, будь то рабочий или нерабочий день (праздник или выходные)... Это будет лучший способ разработать запрос... @Imran

Art Bindu 29.04.2024 16:23

Потерянное рабочее время – это количество потерянных часов, разделенное на общее количество часов, которые необходимо отработать (8 часов в рабочий день). Таким образом, результат конвертируется в часть рабочего дня, если отсутствие менее 8 часов.

Imran 29.04.2024 16:30

@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);

Imran 29.04.2024 16:31

Таблица @Developer по причине: CREATE TABLE TblCoverManagerAbsencesReasons ( intReason int NOT NULL, txtName Varchar(50) ); INSERT INTO TblCoverManagerAbsencesReasons (intReason, txtName) ЗНАЧЕНИЯ (1, 'Болезнь')

Imran 29.04.2024 16:32

Можете ли вы исправить таблицы и добавить, чтобы я мог проверить dbfiddle.uk/vCi8cAR9

Developer 29.04.2024 18:32

@Developer — исправлено и обновлено: dbfiddle.uk/kmiVb7dS

Imran 01.05.2024 11:36
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
18
105
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Спасибо, что дали мне возможность изучить функцию 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. У меня есть решение, которое я прикреплю ниже.

Imran 01.05.2024 10:35

Да, потому что в запросе предоставлена ​​вся относительная информация. По вашим входным данным я создаю запрос. Этот запрос даст вам подход, как решить эту проблему. Итак, мой подход и ваши требования составляют идеальный запрос. Если ваша проблема решена, пожалуйста, примите ответ, закройте заявку

Art Bindu 01.05.2024 12:00

@Имран прочитай эту статью и загрузи свой вопрос: stackoverflow.com/help/how-to-ask

Art Bindu 01.05.2024 12:04
Ответ принят как подходящий

Следующее, кажется, решает мою проблему частичного отсутствия в последний день. Комментарий включен:

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. Возможно, это правильный ответ, но было бы очень полезно предоставить дополнительные пояснения к вашему коду, чтобы разработчики могли понять ваши рассуждения. Это особенно полезно для новых разработчиков, которые не так хорошо знакомы с синтаксисом или пытаются понять концепции. Не могли бы вы отредактировать свой ответ, включив в него дополнительную информацию на благо сообщества?

Jeremy Caney 03.05.2024 00:46

Другие вопросы по теме