Загрузка данных


SET search_path = study;

-- 3.1.1. Для каждого месяца 2024 года выведите количество проектов

WITH months AS (
    SELECT 'январь' AS month_nm, 1 AS month_nus
    UNION ALL SELECT 'февраль', 2
    UNION ALL SELECT 'март', 3
    UNION ALL SELECT 'апрель', 4
    UNION ALL SELECT 'май', 5
    UNION ALL SELECT 'июнь', 6
    UNION ALL SELECT 'июль', 7
    UNION ALL SELECT 'август', 8
    UNION ALL SELECT 'сентябрь', 9
    UNION ALL SELECT 'октябрь', 10
    UNION ALL SELECT 'ноябрь', 11
    UNION ALL SELECT 'декабрь', 12
)
SELECT m.month_nus,
       m.month_nm,
       COUNT(p.id) AS project_cnt
FROM months m
LEFT JOIN projects p
       ON m.month_nus = EXTRACT(MONTH FROM p.start_date)
      AND EXTRACT(YEAR FROM p.start_date) = 2024
GROUP BY m.month_nus, m.month_nm
ORDER BY m.month_nus;


-- 3.2.1. Расставьте порядковый номер проекта для каждого дня

SELECT id,
       project_name,
       start_date,
       ROW_NUMBER() OVER (
           PARTITION BY DATE_TRUNC('month', start_date)
           ORDER BY EXTRACT(DAY FROM start_date), id
       ) AS rn
FROM projects
ORDER BY start_date, rn;


-- 3.2.2. Измените запрос с применением функций RANK(), DENSE_RANK(), NTILE()

SELECT id,
       project_name,
       start_date,
       ROW_NUMBER() OVER w AS rn,
       RANK() OVER w AS rnk,
       DENSE_RANK() OVER w AS dense_rnk,
       NTILE(3) OVER w AS ntile_3
FROM projects
WINDOW w AS (
    PARTITION BY DATE_TRUNC('month', start_date)
    ORDER BY EXTRACT(DAY FROM start_date)
)
ORDER BY start_date, rn;


-- 3.2.3. Напишите аналог запроса из п. 3.1.1 с применением оконной функции

WITH months AS (
    SELECT 'январь' AS month_nm, 1 AS month_nus
    UNION ALL SELECT 'февраль', 2
    UNION ALL SELECT 'март', 3
    UNION ALL SELECT 'апрель', 4
    UNION ALL SELECT 'май', 5
    UNION ALL SELECT 'июнь', 6
    UNION ALL SELECT 'июль', 7
    UNION ALL SELECT 'август', 8
    UNION ALL SELECT 'сентябрь', 9
    UNION ALL SELECT 'октябрь', 10
    UNION ALL SELECT 'ноябрь', 11
    UNION ALL SELECT 'декабрь', 12
)
SELECT DISTINCT
       m.month_nus,
       m.month_nm,
       COUNT(p.id) OVER (
           PARTITION BY m.month_nus
       ) AS project_cnt
FROM months m
LEFT JOIN projects p
       ON m.month_nus = EXTRACT(MONTH FROM p.start_date)
      AND EXTRACT(YEAR FROM p.start_date) = 2024
ORDER BY m.month_nus;


-- 3.2.4. Напишите оконную функцию для присвоения номера каждому студенту в группе с сортировкой по фамилии и имени

SELECT s.group_number,
       ROW_NUMBER() OVER (
           PARTITION BY s.group_number
           ORDER BY s.last_name, s.first_name
       ) AS num_in_group,
       s.last_name,
       s.first_name
FROM students s
ORDER BY s.group_number, num_in_group;


-- 3.2.5. Найдите в каждом месяце начало работы над проектами указанной команды

SELECT DISTINCT
       c.team,
       DATE_TRUNC('month', p.start_date)::date AS month_start,
       FIRST_VALUE(p.project_name) OVER w AS first_project,
       FIRST_VALUE(p.start_date) OVER w AS work_start,
       COUNT(*) OVER (
           PARTITION BY DATE_TRUNC('month', p.start_date)
       ) AS projects_in_month
FROM projects p
JOIN team c ON c.idkom = p.idkom
WHERE p.idkom = 1
WINDOW w AS (
    PARTITION BY DATE_TRUNC('month', p.start_date)
    ORDER BY p.start_date, p.id
)
ORDER BY month_start;


-- 3.2.6. Найдите разницу между стоимостью проекта и средней стоимостью всех проектов

SELECT id,
       project_name,
       price::numeric AS price,
       ROUND(AVG(price::numeric) OVER (), 2) AS avg_price,
       ROUND(
           price::numeric - AVG(price::numeric) OVER (),
           2
       ) AS diff_from_avg
FROM projects
ORDER BY diff_from_avg DESC;


-- 3.2.7. Рассчитайте ранг проекта по баллам и накопительную сумму баллов в рамках трека с использованием поименованного окна

WITH project_score AS (
    SELECT p.id,
           p.project_name,
           p.project_type,
           COALESCE(SUM(t.score), 0) AS total_score
    FROM projects p
    LEFT JOIN task t ON t.idproject = p.id
    GROUP BY p.id, p.project_name, p.project_type
)
SELECT project_type AS track,
       project_name,
       total_score,
       RANK() OVER w AS rank_in_track,
       SUM(total_score) OVER (
           w ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
       ) AS cum_score_in_track
FROM project_score
WINDOW w AS (
    PARTITION BY project_type
    ORDER BY total_score DESC
)
ORDER BY track, rank_in_track;


-- 3.2.8. Рассчитайте нарастающую сумму баллов проектов с использованием фреймов ROWS и RANGE

WITH project_score AS (
    SELECT p.id,
           p.project_name,
           COALESCE(SUM(t.score), 0) AS total_score
    FROM projects p
    LEFT JOIN task t ON t.idproject = p.id
    GROUP BY p.id, p.project_name
)
SELECT project_name,
       total_score,
       SUM(total_score) OVER (
           ORDER BY total_score, id
           ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
       ) AS cum_rows,
       SUM(total_score) OVER (
           ORDER BY total_score
           RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
       ) AS cum_range
FROM project_score
ORDER BY total_score, id;


-- 3.2.9. Найдите накопительную сумму стоимости проектов для каждого статуса

SELECT status,
       project_name,
       start_date,
       price::numeric AS price,
       SUM(price::numeric) OVER (
           PARTITION BY status
           ORDER BY start_date, id
           ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
       ) AS cum_price
FROM projects
ORDER BY status, start_date, id;


-- 3.2.10. Рассчитайте скользящее среднее оценок проектов за текущий и два предыдущих проекта по дате начала

WITH project_score AS (
    SELECT p.id,
           p.project_name,
           p.start_date,
           AVG(t.score::numeric) AS avg_score
    FROM projects p
    LEFT JOIN task t ON t.idproject = p.id
    GROUP BY p.id, p.project_name, p.start_date
)
SELECT project_name,
       start_date,
       avg_score,
       ROUND(
           AVG(avg_score) OVER (
               ORDER BY start_date, id
               ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
           ),
           2
       ) AS moving_avg_3
FROM project_score
ORDER BY start_date, id;


-- 3.2.12. Найдите сумму стоимости и среднюю оценку проектов за последние три месяца относительно даты начала проекта

WITH project_score AS (
    SELECT p.id,
           p.project_name,
           p.start_date,
           p.price,
           AVG(t.score::numeric) AS avg_score
    FROM projects p
    LEFT JOIN task t ON t.idproject = p.id
    GROUP BY p.id, p.project_name, p.start_date, p.price
)
SELECT project_name,
       start_date,
       price,
       avg_score,
       SUM(price) OVER w3 AS sum_price_3m,
       ROUND(AVG(avg_score) OVER w3, 2) AS avg_score_3m,
       COUNT(*) OVER w3 AS projects_3m
FROM project_score
WINDOW w3 AS (
    ORDER BY start_date
    RANGE BETWEEN INTERVAL '3 months' PRECEDING AND CURRENT ROW
)
ORDER BY start_date;