TGViewer
Продуктовый взгляд | Аналитика данных Продуктовый взгляд | Аналитика данных @prodanalysis · 2.3K subscribers
Post #89 5.74K
Оптимизации SQL

Оптимизация SQL в основном это уменьшение числа читаемых строк и корректная работа планировщика с индексами, а не сложный синтаксис запросов. Типичный кейс: большая таблица orders с полями user_id, created_at, status, amount и ежедневный отчёт по выручке за последние 30 дней. Наивный запрос:


SELECT
user_id,
SUM(amount) AS revenue
FROM orders
WHERE created_at >= now() - interval "30 days"
AND status IN ("paid", "completed")
GROUP BY user_id
ORDER BY revenue DESC
LIMIT 100;


По мере роста данных планировщик часто делает Seq Scan, потому что фильтр мало селективен и разрозненные индексы по created_at и status не помогают. Практичный подход — спроектировать один составной и покрывающий индекс под шаблон запроса:


CREATE INDEX CONCURRENTLY idx_orders_status_created_user
ON orders (status, created_at, user_id)
INCLUDE (amount);


Фильтры и JOIN должны быть sargable: столбцы используются напрямую, без обёртки в функции. Вариант WHERE date_trunc("day", created_at) >= current_date - interval "30 days" часто ломает использование индекса по created_at, и в таком случае либо переписывают условие на диапазон по created_at, либо заводят функциональный индекс:


CREATE INDEX CONCURRENTLY idx_orders_created_day
ON orders (date_trunc("day", created_at));


Во многих системах часть нагрузки создаёт ORM через шаблон N+1, когда вместо одного агрегирующего запроса выполняются десятки маленьких. Чаще всего их можно заменить одним запросом с JOIN и агрегацией:


SELECT
u.id,
u.name,
SUM(o.amount) AS revenue_30d
FROM users u
JOIN orders o
ON o.user_id = u.id
WHERE o.created_at >= now() - interval "30 days"
AND o.status IN ("paid", "completed")
GROUP BY u.id, u.name
ORDER BY revenue_30d DESC
LIMIT 100;


Под рабочие запросы разумно делать частичные индексы только по нужным данным, например:


CREATE INDEX CONCURRENTLY idx_orders_paid_recent
ON orders (created_at, user_id)
WHERE status IN ("paid", "completed");


Ключевая идея при любой оптимизации - смотреть планы через EXPLAIN (ANALYZE, BUFFERS) и сравнивать их до и после изменений: тип скана, количество строк, время выполнения. Получается, что при написании эффективного кода мы выделяем важные запросы, делаем под них индексы с продуманным порядком колонок, формулируем условия в sargable виде и избегаем N+1 и тяжёлых агрегаций по сырым данным.

А также напоминаем, что уже скоро стартует наш курс по SQL, где вас научат писать сложные, эффективные запросы с нуля, а также покажут основы устройства и проектирования БД😎

@ProdAnalysis
  • ❤ 2
  • 🔥 2
More from @prodanalysis
  1. Sep 22, 2026Яндекс изменил отбор на аналитиков Раньше процесс состоял из технической секции, алгособес…
  2. Sep 18, 2026Хороший маркетинг обязан нравиться всем? Кейс Сидни Суини говорит, что нет Последние пару…
  3. Sep 16, 2026Если собираетесь на собеседование — держите полезную подборку Хотим поделиться каналом «Ги…
  4. Sep 14, 2026Что происходит с ML после того, как модель обучена Рекомендуем канал Data New Gold. Владим…
  5. Sep 14, 2026Товарищи, Поступашкам нужны контент мейкеры в основной канал по аналитике и другим дисципл…
  6. Sep 13, 2026Можно хорошо считать и всё равно не понимать, зачем нужна твоя работа В рамках открытой не…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →