linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Главы
  • How it worksскоро
  • Уроки
  • База знаний
  • Собеседование
home/postgres/kb/Эксплуатация и наблюдаемость/anti-patterns

kb/ops ── Эксплуатация и наблюдаемость ── intermediate

Каталог анти-паттернов (cheat sheet)

Набор ошибок, которые компилируются и проходят тесты, но врут на проде. NOT IN с NULL глотает строки. BETWEEN по времени ловит лишнюю границу. COUNT по nullable-колонке считает не то. Целочисленное деление режет дробь. char(n), money и timestamp без TZ создают проблемы на пустом месте.

view as markdownaka: sql-mistakes, anti-pattern

Ошибки, которые не падают, а врут

Худший сорт ошибок - не те, что роняют запрос, а те, что молча возвращают неверный результат. Ниже - типовые. Каждая «работает» на демо-данных и проявляется на реальных.

NOT IN + NULL

Если в подзапросе появится хоть один NULL, NOT IN вернёт пусто или выкинет строки - из-за трёхзначной логики SQL.

sql
-- НЕ ТАК: один NULL в списке - и результат пустой
SELECT * FROM flights
WHERE flight_id NOT IN (SELECT flight_id FROM tickets);
-- ТАК: устойчиво к NULL и обычно ещё и быстрее
SELECT * FROM flights f
WHERE NOT EXISTS (SELECT 1 FROM tickets t WHERE t.flight_id = f.flight_id);

Правило: для «нет среди» бери NOT EXISTS, а не NOT IN.

BETWEEN по времени

BETWEEN включает обе границы. Для дат это ловушка:

sql
-- НЕ ТАК: поймает и полночь следующего дня
WHERE booked_at BETWEEN '2026-01-01' AND '2026-01-31'
-- ТАК: полуинтервал [начало, начало следующего)
WHERE booked_at >= '2026-01-01' AND booked_at < '2026-02-01'

Полуоткрытый интервал >= ... < ... не зависит от того, есть ли в дне доли секунды, и не считает одну запись дважды на стыке периодов.

COUNT по nullable-колонке

COUNT(*) считает строки. COUNT(col) считает строки, где col НЕ NULL. Это разные числа, и второе часто пишут, имея в виду первое.

sql
SELECT COUNT(*)        FROM tickets;  -- все строки
SELECT COUNT(passenger) FROM tickets;  -- только где passenger IS NOT NULL

Целочисленное деление

int / int даёт int: дробная часть отбрасывается до всякого округления.

sql
SELECT 5 / 2;            -- 2, не 2.5
SELECT 5 / 2.0;          -- 2.5
SELECT 5::numeric / 2;   -- 2.5000000000000000

Считаешь доли, проценты, средние - приводи к numeric/float хотя бы один операнд заранее.

Типы, которые создают проблемы на ровном месте

  • char(n) дополняет значение пробелами до длины n и сравнивается с усечением хвостовых пробелов. Сюрпризы при сравнении и конкатенации. Бери text или varchar - в PostgreSQL они не медленнее.
  • money зависит от локали lc_monetary и плохо переносится. Для денег - numeric(p, s) с явной точностью.
  • timestamp без time zone хранит «голое» время без привязки к зоне. На сервере и клиенте в разных зонах получишь разные толкования одного значения. По умолчанию бери timestamptz.
  • serial - это старый синтаксический сахар над sequence; у него есть нюансы с правами и зависимостями. В новом коде - стандартный GENERATED ALWAYS AS IDENTITY.

Почему WHERE col::date = ... мешает индексу - это про sargability (см. sargability) и планировщик. Как находить такие запросы в проде - pg-stat-map.

§ команды

bash
SELECT 5 / 2, 5 / 2.0;

Целочисленное против дробного деления: 2 и 2.5

bash
SELECT COUNT(*), COUNT(passenger) FROM tickets;

COUNT(*) считает строки, COUNT(col) - только не-NULL значения

bash
SELECT 'a'::char(3) = 'a  ';

char(n) дополняет пробелами - сравнение игнорирует хвостовые пробелы

§ см. также

  • pg-stat-mapКарта pg_stat_* и pg_stat_statementsСистемные представления pg_stat_* - встроенный мониторинг PostgreSQL. pg_stat_activity показывает, кто что делает прямо сейчас; pg_stat_*_tables и pg_statio_* - накопленную статистику по таблицам; pg_stat_statements - агрегат по нормализованным запросам, главный инструмент поиска медленного SQL.
  • memory-configКонфигурация памяти в контексте механизмаЧетыре параметра памяти решают разные задачи. shared_buffers - общий кеш страниц на весь кластер. work_mem - лимит на ОДНУ операцию сортировки или хеша в ОДНОМ запросе, и его легко умножить на сотни. maintenance_work_mem - для vacuum и build индекса. effective_cache_size - подсказка планировщику, не аллокация.
  • engine-securityБезопасность движка: pg_hba, роли, SECURITY DEFINERpg_hba.conf решает, кого вообще пускать и как проверять - метод trust пускает без пароля кого попало. Роли несут привилегии. Функция SECURITY DEFINER исполняется с правами владельца, и при незакреплённом search_path это готовый канал эскалации привилегий.
  • sargabilitySargability: почему индекс не используетсяУсловие sargable, если индекс по колонке к нему применим. Каст или функция над колонкой (created::date, lower(email)) делают его не-sargable - индекс игнорируется, план уходит в Seq Scan. Лечение - переписать или индекс по выражению.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки