Ответ
1. Сложные аналитические запросы:
- Оконные функции (Window Functions) для расчетов без группировки строк:
ROW_NUMBER(),RANK(),SUM(...) OVER (PARTITION BY ...). - Рекурсивные CTE (Common Table Expressions) для работы с иерархическими данными.
-- Построение дерева подчиненных сотрудников WITH RECURSIVE employee_hierarchy AS ( -- Якорь: начальник верхнего уровня SELECT id, name, manager_id, 1 AS level FROM employees WHERE manager_id IS NULL UNION ALL -- Рекурсивная часть: подчиненные SELECT e.id, e.name, e.manager_id, eh.level + 1 FROM employees e INNER JOIN employee_hierarchy eh ON e.manager_id = eh.id ) SELECT * FROM employee_hierarchy ORDER BY level, id;
2. Управление большими объемами данных:
- Партиционирование таблиц (например, по диапазону дат
RANGE) для ускорения запросов и упрощения управления (удаление старых партиций). - Стратегии шардинга (горизонтального разделения) на уровне приложения.
3. Расширенные возможности:
- Триггеры и правила: Для реализации аудита изменений (
AFTER UPDATE) или сложной бизнес-логики на уровне БД. - Полнотекстовый поиск: Использование типов
tsvectorиtsqueryс индексами GIN для быстрого поиска по тексту. - Работа с геоданными: Использование расширения PostGIS для выполнения пространственных запросов (поиск в радиусе, расчет расстояний).
Ответ 18+ 🔞
А, ну так, значит, ты хочешь из простого ламерского SELECT * FROM users перейти в настоящую лигу, где запросы такие, что сам PostgreSQL иногда офигевает? Ну что ж, пристегнись, сейчас будет немного магии, от которой мозги могут закипеть, если они не смазаны должным образом.
1. Про эти ваши оконные функции, или как считать, не сворачивая всё в кучу.
Смотри, обычный GROUP BY — он как бульдозер: сгребает все строки в кучки и выдает одну строку на кучку. А иногда надо, чтобы все строки на месте остались, но в каждой ещё и какая-то сводная инфа по её «окружению» была. Вот для этого они и нужны, эти OVER().
Ну, например, ROW_NUMBER() — пронумерует строки в указанном порядке. RANK() — тоже пронумерует, но если значения одинаковые, то и номер даст одинаковый, а следующий — пропустит. А самое вкусное — это агрегаты с OVER. Хочешь для каждого сотрудника рядом с его зарплатой видеть среднюю по его отделу? Без проблем, епта!
SELECT
name,
department,
salary,
AVG(salary) OVER (PARTITION BY department) as avg_department_salary
FROM employees;
Видишь? Никакого группирования! Все строки на месте, а в последней колонке — магия. PARTITION BY — это как GROUP BY, но только внутри окна. Охуенно же?
А теперь рекурсивные CTE. Это, блядь, просто песня, когда у тебя древовидные структуры: сотрудники с начальниками, категории товаров с родительскими и т.д. Выглядит страшновато, но принцип простой, ёпта.
-- Построение дерева подчиненных сотрудников
WITH RECURSIVE employee_hierarchy AS (
-- Якорь: начальник верхнего уровня (тот, у кого нет манагера)
SELECT id, name, manager_id, 1 AS level
FROM employees
WHERE manager_id IS NULL
UNION ALL
-- Рекурсивная часть: цепляем подчиненных к начальникам
SELECT e.id, e.name, e.manager_id, eh.level + 1
FROM employees e
INNER JOIN employee_hierarchy eh ON e.manager_id = eh.id
)
SELECT * FROM employee_hierarchy ORDER BY level, id;
Работает так: сначала берём «якорь» — верхушку иерархии. Потом рекурсивно присоединяем (UNION ALL) тех, чей manager_id равен id из уже набранной кучи. И так по кругу, пока новых подчиненных не останется. В итоге получаем всю иерархию с уровнями. Красота, в рот меня чих-пых!
2. Когда данных овердохуища, и всё тормозит.
Тут два главных козыря. Партиционирование — это когда ты одну логическую таблицу разбиваешь на много физических кусков (партиций) по какому-то правилу. Чаще всего по дате. Запрос SELECT * FROM sales WHERE sale_date >= '2024-01-01' будет сканировать только партицию за 2024 год, а не всю таблицу с 1990-го. А удалить старые данные — вообще сказка: DROP TABLE sales_y2023. Бац — и миллионы строк испарились, не нагружая DELETE.
Шардинг — это уже посерьёзнее. Когда одна база уже не тянет, данные горизонтально раскидывают по нескольким серверам. Сам PostgreSQL из коробки этого не умеет, тут надо городить огород на уровне приложения или использовать всякие Citus. Это уже высший пилотаж, где можно и по пизде получить, если не спроектировать ключ шардирования правильно.
3. Разные ништяки, о которых не все знают.
- Триггеры и правила. Нужно логировать ВСЕ изменения в критичной таблице?
AFTER UPDATE OR INSERT OR DELETEтриггер — твой друг. Но, блядь, осторожно! Навешаешь много сложной логики — и производительность накроется медным тазом. Триггеры — это как острый нож: в умелых руках инструмент, а так — себе же хуже сделаешь. - Полнотекстовый поиск.
LIKE '%что-то%'— это для школьников и маленьких табличек. Для больших текстов естьto_tsvector()иto_tsquery(). Ты преобразуешь текст в вектор слов (лемм), создаёшь GIN-индекс и потом ищешь молниеносно, с учётом словоформ, стоп-слов и всего такого. Это не просто поиск подстроки, это уже почти Google на минималках. - Геоданные с PostGIS. Это вообще отдельная вселенная. «Найди все аптеки в радиусе 500 метров от меня» — одна строчка запроса. Расстояния между точками, площади полигонов, пересечения — всё на уровне БД. Ставишь расширение — и твоя база превращается в геоинформационную систему. Выглядит как магия, но, сука, работает.
Вот так-то. Освоишь это — и перестанешь быть просто «писалкой запросов», станешь тем самым волшебником, который может вытащить из базы вообще всё, что угодно, да ещё и быстро. Главное — не увлекайся слишком сложными конструкциями, а то поддерживать этот пиздец будет некому, кроме тебя.