SQL-инструменты, использованные в проекте
Описание
В итоговом проекте по базе данных stroy использовались разные механизмы PostgreSQL — от базовой агрегации до оконной аналитики, рекурсивных запросов и материализованных представлений.
Этот документ систематизирует ключевые SQL-приёмы, применённые при решении задач.
1. Агрегатные функции
Агрегатные функции использовались для получения итоговых количественных показателей.
В проекте применялись:
– count(*) — подсчёт количества проектов;
– sum(...) — расчёт сумм платежей, окладов, стоимости проектов и бонусов;
– avg(...) — расчёт среднего возраста сотрудников;
– max(...) — определение максимального размера бонуса.
Примеры задач:
– количество проектов, подписанных в 2023 году;
– сумма полученных платежей от контрагентов из заданного города;
– средний возраст уволенных сотрудников;
– максимальный бонус руководителя проекта.
2. Обработка возможных NULL
Для ситуаций, когда агрегатная функция может вернуть null, использовалась функция:
coalesce(...)
Примеры:
coalesce(avg(...), 0)
coalesce(sum(pp.amount), 0)
Это позволяет возвращать ожидаемое числовое значение 0, если выборка оказалась пустой.
Применение:
– средний возраст уволенных сотрудников;
– сумма платежей;
– сумма окладов сотрудников.
3. Работа с датами и интервалами
В проекте использовались функции и операции для анализа временных показателей.
extract
Используется для получения года из даты или timestamp:
extract(year from sign_date)
extract(year from fact_transaction_timestamp)
Применение:
– фильтрация проектов по году подписания;
– разбиение платежей по годам;
– расчёт полных лет возраста.
age
Используется для расчёта возраста человека:
age(current_date, birthdate)
Применение:
– суммарный возраст сотрудников;
– средний возраст в полных годах.
Интервальная фильтрация по датам
В проекте используется фильтрация через границы периода:
hire_date >= date '2022-01-01'
and hire_date < date '2023-01-01'
Такой подход корректно выделяет записи за конкретный календарный год и избегает неоднозначностей.
4. Строковая фильтрация
Для поиска сотрудника по условиям на фамилию использовались строковые операции.
like
last_name like 'М%'
Позволяет выбрать фамилии, начинающиеся на нужную букву.
length
length(last_name) = 8
Позволяет ограничить длину фамилии.
Конкатенация строк
first_name || ' ' || last_name
Используется для формирования имени и фамилии в одном текстовом столбце.
5. Соединение таблиц
Проект активно использует join для объединения данных из разных сущностей.
Пример многоступенчатой цепочки
project_payment
→ project
→ customer
→ address
→ city
→ country
Она использовалась для расчёта суммы платежей от контрагентов из конкретного города и страны.
Пример проектной цепочки
project
→ employee
→ person
Она применялась для получения ФИО руководителя проекта.
Проект демонстрирует умение:
– выбирать корректный тип соединения;
– строить запросы через несколько уровней связей;
– использовать left join, когда связанные данные могут отсутствовать.
6. Исключающая логика через NOT EXISTS
Для поиска уволенных сотрудников, которые не задействованы на проектах, использовался подзапрос:
not exists (
select 1
from project pr
where e.employee_id = any(pr.employees_id)
or e.employee_id = pr.project_manager_id
)
Этот подход позволяет исключить сотрудников, которые:
– указаны в составе участников проекта;
– являются руководителями проектов.
Что демонстрирует:
– проверку отсутствия связанных данных;
– аккуратную обработку нескольких условий участия;
– применение подзапроса в логике фильтрации.
7. Работа с массивами через ANY
В таблице project сотрудники, задействованные на проекте, хранятся в массиве employees_id.
Для проверки наличия сотрудника в массиве используется оператор:
e.employee_id = any(pr.employees_id)
Это позволяет определить, входит ли сотрудник в команду проекта.
Применение:
– исключение сотрудников, которые уже участвовали в проектах.
8. Общие табличные выражения — CTE
Для повышения читаемости и декомпозиции сложной логики применялись CTE:
with manager_bonus as (...)
select ...
CTE позволяли:
– разделять сложный запрос на этапы;
– упростить чтение решения;
– сначала подготовить промежуточные данные, а потом использовать их в итоговой выборке.
Примеры задач:
– расчёт бонусов руководителей;
– накопительные авансовые платежи;
– комплексный запрос со скользящим средним;
– формирование материализованного представления.
9. Оконная функция ROW_NUMBER
Функция:
row_number() over (...)
использовалась в двух задачах.
Нумерация платежей по годам
row_number() over (
partition by extract(year from fact_transaction_timestamp)
order by fact_transaction_timestamp, project_payment_id
)
Позволяет сделать сквозную нумерацию платежей отдельно внутри каждого года.
Выбор последнего платежа по проекту
row_number() over (
partition by project_id
order by fact_transaction_timestamp desc,
project_payment_id desc
)
Позволяет определить самый поздний фактический платёж по каждому проекту.
10. Накопительный итог через SUM() OVER
Для расчёта планируемых авансовых платежей использовалась оконная сумма:
sum(amount) over (
partition by date_trunc('month', plan_payment_date)
order by plan_payment_date, project_payment_id
)
Что это даёт:
– суммы считаются последовательно по датам;
– накопление начинается заново в каждом месяце;
– можно определить момент первого превышения порога.
Применение:
– поиск даты, когда накопительная сумма превысила 30 000 000.
11. Функция LAG
Для сравнения текущего накопительного значения с предыдущим применялась функция:
lag(accumulation) over (...)
Она позволила определить момент, когда сумма впервые превысила контрольное значение.
Логика отбора:
accumulation > 30000000
and coalesce(previous_accumulation, 0) <= 30000000
12. Скользящее среднее через AVG() OVER
В сложной аналитической задаче использовался расчёт скользящего среднего платежей:
avg(amount) over (
order by fact_transaction_timestamp, project_payment_id
rows between 2 preceding and 2 following
)
Окно включает:
– две строки перед текущей;
– текущую строку;
– две строки после текущей.
Это позволяет рассчитать усреднённое значение платежей в локальном окружении каждой строки.
13. Рекурсивный CTE
Для обхода иерархии структурных подразделений использовалась рекурсия:
with recursive units as (
select unit_id
from company_structure
where unit_id = 17
union all
select cs.unit_id
from company_structure cs
join units u on u.unit_id = cs.parent_id
)
Этот подход позволяет:
– начать с выбранного подразделения;
– последовательно найти всех потомков;
– использовать полученный список в последующем расчёте окладов.
14. Агрегация строк через STRING_AGG
В итоговом отчёте нужно было вывести названия типов работ контрагента в одной строке.
Использовалось:
string_agg(
tw.type_of_work_name,
', '
order by tw.type_of_work_name
)
Результат:
Проектирование, Строительство, Согласование документации
Применение:
– материализованное представление по проектам и контрагентам.
15. Материализованное представление
Финальная задача проекта включает создание объекта:
create materialized view project_payment_report as
...
Материализованное представление используется для хранения готового отчёта, который объединяет:
– проект;
– последний фактический платёж;
– руководителя;
– контрагента;
– типы работ.
В отличие от обычного представления, материализованное представление физически сохраняет результат запроса и может использоваться как предварительно рассчитанная отчётная таблица.
16. LEFT JOIN для сохранения проектов без платежей
При создании итогового отчёта используется:
left join last_project_payment lpp
on lpp.project_id = pr.project_id
Это позволяет сохранить в отчёте проекты даже в том случае, если по ним ещё не было фактических оплат.
Такой выбор типа соединения делает отчёт полнее и предотвращает случайную потерю строк.
17. Декомпозиция сложных запросов
Особенно сложные задачи проекта решались не монолитно, а через последовательные этапы.
Например, задача 9 логически разделена на:
-
нумерацию платежей;
-
отбор каждого пятого платежа;
-
расчёт скользящих средних;
-
суммирование скользящих средних;
-
расчёт годовой стоимости проектов;
-
сравнение агрегатов.
Такой подход:
– упрощает чтение запроса;
– облегчает проверку;
– снижает риск логических ошибок;
– делает решение объяснимым на защите.
Итоговый перечень применённых SQL-инструментов
| Категория | Инструменты |
|---|---|
| Агрегации | count, sum, avg, max |
Обработка NULL |
coalesce |
| Даты | age, extract, интервальная фильтрация |
| Строки | like, length, || |
| Подзапросы | not exists, вложенный select max(...) |
| Массивы | any |
| CTE | with |
| Рекурсия | with recursive |
| Оконные функции | row_number, lag, sum() over, avg() over |
| Диапазон окон | rows between 2 preceding and 2 following |
| Строковая агрегация | string_agg |
| Отчётные объекты | materialized view |