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/lessons/pg-lab-43-1-anti-patterns

lesson ── postgres-labs ── ~22 мин ── 6 шагов

Ловушки SQL: воспроизведи и перепиши

Худшие ошибки в SQL не падают, а молча врут. Ты воспроизведёшь четыре классические ловушки на маленькой таблице и перепишешь главную из них на корректный вариант. Для каждой сначала предскажи результат, потом проверь

  • и увидишь, что «работающий» запрос возвращает не то.

▶ интерактивный sandbox

Поднимется контейнер postgreslab/postgres-base с PostgreSQL 17 и psql. В браузере откроется терминал, база lab уже настроена. Каждый шаг проверяется автоматически. Сеть air-gapped, наружу контейнер не ходит.

запустить sandbox →

stack ── PostgreSQL 17 · psql · 1 GB RAM · air-gapped · самоуничтожается через 45 мин простоя

Шаги

  1. 01

    Целочисленное деление режет дробь

    Предскажи вывод, потом выполни:

    sql
    SELECT 5 / 2;

    Ожидал 2.5? В SQL int / int даёт int - дробь отброшена ещё до всякого округления.

    ✓ 5/2 = 2, а не 2.5. Дробь отброшена.

  2. 02

    Почини делением в numeric

    Приведи хотя бы один операнд к numeric до деления:

    sql
    SELECT 5::numeric / 2;

    Предскажи: теперь будет 2.5 (с длинным хвостом нулей - это точность numeric).

    ✓ Приведение к numeric вернуло дробный результат.

  3. 03

    COUNT по nullable-колонке считает не строки

    Создай таблицу, где часть значений NULL:

    sql
    CREATE TABLE ap_demo AS
    SELECT g AS id, CASE WHEN g <= 3 THEN 'x' END AS note
    FROM generate_series(1, 6) AS g;

    Предскажи вывод SELECT COUNT(*), COUNT(note) FROM ap_demo;. COUNT(*) считает строки, COUNT(note) - только где note НЕ NULL.

    ✓ 6 строк, но только 3 не-NULL note. Это разные числа.

  4. 04

    char(n) усекает пробелы при сравнении

    Предскажи результат, потом проверь:

    sql
    SELECT ('a'::char(3) = 'a  ')::text;

    char(3) дополняет 'a' пробелами до длины 3, а при сравнении хвостовые пробелы усекаются - поэтому равенство истинно. Сюрприз на ровном месте.

    ✓ Сравнение истинно - char(n) ведёт себя неочевидно. Бери text/varchar.

  5. 05

    NOT IN с NULL глотает все строки

    Добавь в таблицу строку с NULL в id:

    sql
    INSERT INTO ap_demo (id, note) VALUES (NULL, 'z');

    Теперь предскажи: сколько вернёт запрос ниже? Кажется, что 4 (числа 7,8,9,10 не входят в id 1..6). Но один NULL в подзапросе всё ломает:

    sql
    SELECT count(*) FROM generate_series(1, 10) AS g
    WHERE g NOT IN (SELECT id FROM ap_demo);

    ✓ Из-за NULL результат 0, а не 4. NOT IN солгал.

  6. 06

    Перепиши на NOT EXISTS

    Замени NOT IN на устойчивый NOT EXISTS:

    sql
    SELECT count(*) FROM generate_series(1, 10) AS g
    WHERE NOT EXISTS (SELECT 1 FROM ap_demo a WHERE a.id = g);

    Предскажи: теперь честные 4 (числа 7,8,9,10). NOT EXISTS не страдает от NULL. Когда закончишь, можешь убрать таблицу: DROP TABLE ap_demo;.

    ✓ NOT EXISTS вернул верные 4. Вот так и пишут «нет среди».

Что ты узнал

Анти-паттерны опасны тем, что не падают, а возвращают неверный результат. NOT IN с NULL даёт пусто - бери NOT EXISTS. int/int режет дробь - приводи к numeric. COUNT(col) считает не-NULL - для строк COUNT(*). char(n) усекает пробелы при сравнении - бери text.

команды

  • SELECT 5::numeric / 2;дробное деление вместо целочисленного 5/2
  • SELECT COUNT(*), COUNT(col) FROM t;все строки против не-NULL значений
  • ... WHERE NOT EXISTS (SELECT 1 FROM t WHERE ...);устойчивая замена NOT IN

концепции

  • · NOT IN с NULL в подзапросе возвращает пусто - всегда NOT EXISTS
  • · int / int отбрасывает дробь до округления - приводи операнд к numeric
  • · COUNT(*) считает строки, COUNT(col) - только не-NULL

← предыдущая

Архив WAL и привязка момента к сегменту

следующая →

search_path и SECURITY DEFINER: воспроизведи подмену

Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки