Какие сложности возникают при написании SQL-запросов
Что проверяют
Писали ли вы SQL руками на живых данных или только через ORM на десяти строках в тестовой базе. По выбору сложностей видно ваш потолок: кто-то вспоминает синтаксис JOIN, кто-то планы выполнения и блокировки. Следом обычно просят разобрать конкретный медленный запрос.
Как отвечать
Идите от причин, а не от списка симптомов:
- план вместо догадок:
EXPLAIN (ANALYZE, BUFFERS)в PostgreSQL показывает фактическое время, число строк и чтение с диска. Расхождение оценки и факта в разы это обычно устаревшая статистика или промах в селективности; - индекс, который не применяется: функция над колонкой (
WHERE date(created_at) = ...) отключает индекс, лечится переписыванием условия в диапазон или функциональным индексом; - NULL в логике:
NOT INс подзапросом, где встретилсяNULL, вернёт пусто. Это трёхзначная логика, безопаснееNOT EXISTS; - дубликаты после соединения:
JOINодин ко многим размножает строки, иSUMпо такому результату завышает суммы. Агрегировать надо до соединения, в подзапросе или черезLATERAL; - пагинация:
OFFSET 100000заставляет базу отбросить сто тысяч строк, выручает keyset-пагинация по последнему ключу; - конкурентность: длинные транзакции держат блокировки и раздувают таблицу, очередь задач безопаснее разбирать через
SELECT ... FOR UPDATE SKIP LOCKED.
Если работаете через ORM, скажите про N+1 и чем его чините: selectinload в SQLAlchemy, select_related и prefetch_related в Django, плюс логирование сырых запросов, чтобы видеть, что реально ушло в базу.
Где спотыкаются
Сводят всё к «надо добавить индекс». Индекс замедляет запись, занимает место, а на выборке, возвращающей половину таблицы, планировщик его и не возьмёт.
Считают CTE способом ускорить запрос. В PostgreSQL начиная с 12-й версии обычный WITH встраивается в основной запрос, а MATERIALIZED наоборот ставит барьер оптимизации. Читаемость от CTE растёт, скорость сама по себе нет.
Называют TRY...CATCH универсальным способом обработки ошибок. Это конструкция T-SQL, в PostgreSQL исключения ловит блок EXCEPTION внутри PL/pgSQL, а в приложении ошибку чаще разбирает драйвер.