#sql #postgresql
#sql #postgresql
Вопрос:
Допустим, есть таблица, в которой хранятся данные о посещаемости учащихся на занятиях. Учащемуся необходимо посещать каждое занятие только один раз в неделю. Используя столбец days_diff, мы решаем, за какие недели студент должен получить кредит. Как обычно, каждая неделя состоит из 7 дней, а конец недели (в днях) делится на 7. В моем сценарии для недели 3 другой случай: всякий раз, когда посещается week_3, неделя заканчивается в эту дату посещения, а week_4 начинается на следующий день.
Я использую PostgreSQL 11.4!
select * from students_data
имя_студента | class_subject | дата регистрации_date | посещение_дата | days_diff | week_num |
---|---|---|---|---|---|
Фрэнк | Химия | 2021-10-15 | 2021-10-18 | 3 | week_1 |
Фрэнк | Химия | 2021-10-15 | 2021-10-24 | 9 | week_2 |
Фрэнк | Химия | 2021-10-15 | 2021-11-01 | 17 | week_3 |
Фрэнк | Химия | 2021-10-15 | 2021-11-10 | 26 | week_4 |
Фрэнк | Химия | 2021-10-15 | 2021-11-20 | 36 | week_6 |
Дэнни | Физика | 2021-10-20 | 2021-10-25 | 5 | week_1 |
Дэнни | Физика | 2021-10-20 | 2021-11-02 | 13 | week_2 |
Дэнни | Физика | 2021-10-20 | 2021-11-08 | 19 | week_3 |
Дэнни | Физика | 2021-10-20 | 2021-11-23 | 34 | week_5 |
Дэнни | Физика | 2021-10-20 | 2021-11-30 | 41 | week_6 |
Желаемая разбивка по неделям:
student days_in_week week_number
Frank 1-7 week_1
Frank 8-14 week_2
Frank 15-17 week_3 <-----week_3 shakes up the week breakdown
Frank 18-24 week_4
Frank 25-31 week_5........
Danny 1-7 week_1
Danny 8-14 week_2
Danny 15-19 week_3 <----week_3 shakes up the week breakdown
Danny 20-26 week_4
Danny 27-33 week_5........
Запрошено: столбец new_week_num
имя_студента | class_subject | дата регистрации_date | посещение_дата | days_diff | week_num | new_week_num |
---|---|---|---|---|---|---|
Фрэнк | Химия | 2021-10-15 | 2021-10-18 | 3 | week_1 | week_1 |
Фрэнк | Химия | 2021-10-15 | 2021-10-24 | 9 | week_2 | week_2 |
Фрэнк | Химия | 2021-10-15 | 2021-11-01 | 17 | week_3 | week_3 |
Фрэнк | Химия | 2021-10-15 | 2021-11-10 | 26 | week_4 | week_5 |
Фрэнк | Химия | 2021-10-15 | 2021-11-20 | 36 | week_6 | week_7 |
Дэнни | Физика | 2021-10-20 | 2021-10-25 | 5 | week_1 | week_1 |
Дэнни | Физика | 2021-10-20 | 2021-11-02 | 13 | week_2 | week_2 |
Дэнни | Физика | 2021-10-20 | 2021-11-08 | 19 | week_3 | week_3 |
Дэнни | Физика | 2021-10-20 | 2021-11-23 | 34 | week_5 | week_6 |
Дэнни | Физика | 2021-10-20 | 2021-11-30 | 41 | week_6 | week_7 |
Ответ №1:
Решение 1: cte и ОБЪЕДИНЕНИЕ
WITH list AS
(
SELECT student_name, class_subject, registration_date, days_diff
FROM your_table
WHERE week_num = 'week_3'
)
SELECT t.*
, CASE
WHEN t.days_diff <= l.days_diff
THEN t.week_num
ELSE 'week_' || (3 ceil((t.days_diff - l.days_diff)/7.0)) :: text
END AS new_week_num
FROM your_table AS t
LEFT JOIN list AS l
ON t.student_name = l.student_name
AND t.class_subject = l.class_subject
AND t.registration_date = l.registration_date
Решение 2: БОКОВОЕ СОЕДИНЕНИЕ
SELECT t.*
, CASE
WHEN t.days_diff <= l.days_diff
THEN t.week_num
ELSE 'week_' || (3 ceil((t.days_diff - l.days_diff)/7.0)) :: text
END AS new_week_num
FROM your_table AS t
CROSS JOIN LATERAL
( SELECT l.days_diff
FROM your_table AS l
WHERE l.student_name = t.student_name
AND l.class_subject = t.class_subject
AND l.registration_date = t.registration_date
AND l.week_num = 'week_3'
LIMIT 1
) AS l
Решение 3: функция окна
SELECT t.*
, CASE
WHEN days_diff <= (array_agg(days_diff) FILTER (WHERE week_num = 'week_3') OVER w)[1]
THEN week_num
ELSE 'week_' || (3 ceil((t.days_diff - (array_agg(days_diff) FILTER (WHERE week_num = 'week_3') OVER w)[1])/7.0)) :: text
END AS new_week_num
FROM your_table AS t
WINDOW w AS (PARTITION BY student_name, class_subject, registration_date ORDER BY days_diff ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
тест в dbfiddle