ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ : (Этот предназначен для обоих: этот здесь и этот )
демо: db <> fiddle
WITH dates AS( -- 1
SELECT
min(checktime)::date as min,
max(checktime)::date as max
FROM log
)
SELECT
user_id,
check_date::date,
-- 4:
CASE WHEN checktime::date = check_date THEN checktime::time ELSE NULL END as time_in,
CASE WHEN checktime::date = check_date THEN time_out::time ELSE NULL END as time_out,
CASE WHEN checktime::date = check_date THEN (time_out - checktime)::time ELSE NULL END as hours
FROM (
SELECT
user_id,
checktime,
lead(checktime) OVER (ORDER BY checktime) as time_out, -- 2
generate_series( -- 3
(SELECT min FROM dates),
(SELECT max FROM dates),
interval '1 day'
) as check_date
FROM log
)s
ORDER BY user_id, check_date
- Рассчитать минимальные / максимальные пороговые значения даты в вашем журнале, чтобы получить границы для создания даты
lead
оконная функция принимает следующееchecktime
значение для текущей строки.Таким образом, следующее checktime
считается как time_out
generate_series
и генерирует все значения даты между заданными (расчетными) границами CASE
частей: если текущий checktime
равен несгенерированное значение даты затем выдает NULL
;иначе выдайте текущую time_in
/ time_out
/ разницу во времени
Относительно почти дублированного расширенного вопроса:
Использование журнала посещаемости и таблицы дней выключения.Как показать настоящее, отсутствующее и выходной день? Я добавляю ответ сюда, потому что дублирование должно быть закрыто, а вопрос выше должен быть расширен.
демонстрация, см. Вторую часть скрипта выше
WITH dates AS(
SELECT
min(checktime)::date as min,
max(checktime)::date as max
FROM log
)
SELECT DISTINCT ON (user_id, check_date, time_in) -- 6
user_id,
check_date,
to_char(check_date, 'Day') as day, -- 2
COALESCE(time_in, -- 4
MAX(time_in) OVER (PARTITION BY user_id, check_date ORDER BY time_out NULLS LAST)
) as time_in,
time_out,
hours,
CASE -- 5
WHEN checktime::date = check_date THEN 'present'
WHEN of.days IS NOT NULL THEN 'OFF DAY'
ELSE 'absent'
END as status
FROM (
SELECT
user_id,
check_date,
checktime,
CASE WHEN checktime::date = check_date THEN checktime::time ELSE NULL END as time_in,
CASE WHEN checktime::date = check_date THEN time_out::time ELSE NULL END as time_out,
-- 1
CASE WHEN checktime::date = check_date THEN extract(epoch FROM (time_out - checktime)) / 60 / 60 ELSE NULL END as hours
FROM (
SELECT
user_id,
checktime,
lead(checktime) OVER (ORDER BY checktime) as time_out,
generate_series(
(SELECT min FROM dates),
(SELECT max FROM dates),
interval '1 day'
) as check_date
FROM log
) s
) s
--- 3
LEFT JOIN off_days of ON (of.userid = s.user_id) AND (of.days = trim(to_char(check_date, 'day')))
ORDER BY user_id, check_date
Поскольку это расширение предыдущего запроса, я объясняю только изменения:
- Вместо того, чтобы указывать разницу во времени как
time
, необходимо значение numeric
.Так extract(epoch...)
получает секунды разности, которые конвертируются в часы на / 60 / 60
- Преобразование даты в будний день с помощью функции
to_char
- Соединение таблицы off_day с
user_id
и днем недели (снова с использованием функции to_char
, на этот раз с небольшими заглавными буквами).to_char
добавляет пробел - поэтому trim()
удаляет его для сравнения - Хитрая часть (вместе с 6): поскольку объединение дублирует строки, необходимо устранить неправильные,Невозможно сделать простой
DISTINCT
в user_id
и день недели, потому что, например, 152
имеет две записи в один день.Но поскольку 53
имеет две записи в разные дни (в моем примере см. Fiddle), для обеих дат создается действительная и пустая строка.Эта строка кода дублирует значение time_in
в пустой строке (следующий шаг см. (6)) - Если есть запись для сгенерированной даты (см. Первую часть выше), это
present
.Если нет, проверьте, является ли «выходной день», в противном случае absent
- У нас есть случаи: A: нет дублирующихся строк;B: две строки для user_id и дня недели, потому что есть две записи;C: две строки (с пустым временем) из-за соединения.Мы не хотим повторяющихся строк, поэтому есть
distinct
.Это работает также для (C), потому что мы продублировали time_in
для NULL
строк в (4)