Это часть 7 из 10 учебника «SQL с нуля для аналитика». Содержание серии в конце поста. ← Часть 6
TL;DR: NULL означает «нет данных», не равен ничему (даже NULL). Сравнивать через IS NULL / IS NOT NULL. COALESCE(x, default) подменяет NULL на значение. CAST(x AS type) меняет тип. CASE WHEN cond THEN val ... END — условная логика. Эти 4 темы решают 80% data quality проблем.
В этой части:
- Что такое NULL и его 3 правила
- 6 основных типов данных PG
- COALESCE для дефолтных значений
- CAST между типами
- CASE WHEN для условий
Что такое NULL и почему он опасен?
NULL — не «ноль» и не «пустая строка». Это «нет значения».
!NULL — три правила распространения: арифметика, сравнение, агрегаты
Как NULL «портит» всю формулу — и как COALESCE спасает:
!NULL cascade: ошибка распространяется через колонки
3 главных правила:
- NULL = NULL → NULL (не true!). Поэтому
WHERE col = NULLвсегда вернёт 0 строк. - Любая арифметика с NULL = NULL.
5 + NULL = NULL,5 * NULL = NULL. - Агрегаты игнорируют NULL (кроме
COUNT(*)).
-- В таблице 10 строк, у 3 amount IS NULL, у 7 значения 100, 200, ..., 700
SELECT SUM(amount) FROM orders; -- 2800 (7 строк × среднее)
SELECT AVG(amount) FROM orders; -- 400 (среднее по 7, не по 10)
SELECT COUNT(*) FROM orders; -- 10 (все строки)
SELECT COUNT(amount) FROM orders; -- 7 (только не-NULL)
Типичный случай: разработчик добавил колонку без default — все старые строки получили NULL. Аналитик считаетSUM(amount - discount), получает SUM = NULL для старых заказов. Фикс:SUM(amount - COALESCE(discount, 0)).
Как фильтровать NULL?
Только через IS NULL / IS NOT NULL:
-- Удалённые юзеры
SELECT * FROM users WHERE deleted_at IS NOT NULL;
-- Активные юзеры
SELECT * FROM users WHERE deleted_at IS NULL;
WHERE col = NULL или WHERE col != NULL — никогда не делай.
Какие 6 типов данных нужно знать?
| Тип | Что хранит | Пример |
|---|---|---|
| INTEGER / BIGINT | Целое число | id, count |
| NUMERIC(p,s) / DECIMAL | Точное дробное | money, percentage |
| FLOAT / DOUBLE | Приближённое дробное | sensor data |
| TEXT / VARCHAR | Строка | name, email |
| DATE / TIMESTAMP | Дата / дата-время | created_at |
| BOOLEAN | true / false | is_active |
Правило для денег: всегда NUMERIC(18,2) или DECIMAL, не FLOAT. FLOAT теряет копейки из-за binary precision.
-- FLOAT теряет: 0.1 + 0.2 ≠ 0.3 (даёт 0.30000000000000004)
-- NUMERIC точно: 0.10 + 0.20 = 0.30
Как заменить NULL на значение по умолчанию?
COALESCE(val1, val2, ..., default) — возвращает первое не-NULL.
-- Если nickname NULL — показать email
SELECT COALESCE(nickname, email, 'anonymous') AS display_name
FROM users;
-- Если discount NULL — считать 0
SELECT amount - COALESCE(discount, 0) AS final_amount
FROM orders;
Альтернатива COALESCE — NULLIF(a, b) (возвращает NULL если a=b). Реже нужно.
Типичный случай: дашборд CEO «средняя выручка на пользователя» показывает NULL первое число месяца — потому что среди новых пользователей нет ещё заказов. Фикс: COALESCE(AVG(revenue), 0).
Как преобразовать тип через CAST?
CAST(value AS type) или короткая форма value::type (PG-специфика):
-- Строка в число
SELECT CAST('123' AS INTEGER);
SELECT '123'::INTEGER; -- PG syntax
-- Число в строку
SELECT CAST(123 AS TEXT);
SELECT 123::TEXT;
-- Точное дробное
SELECT CAST(amount AS NUMERIC(18,2));
SELECT amount::NUMERIC(18,2);
-- Дата
SELECT CAST('2026-06-02' AS DATE);
SELECT '2026-06-02'::DATE;
Подвох: если 'abc' нельзя сконвертировать в INTEGER — runtime error. Используй TRY_CAST (MS SQL) или SAFE_CAST (BigQuery) для безопасной конверсии.
Что такое CASE WHEN и когда его использовать?
CASE WHEN cond1 THEN val1 WHEN cond2 THEN val2 ELSE valN END — условная логика, как if/elif/else.
-- Категоризация юзеров по выручке
SELECT
user_id,
SUM(amount) AS total,
CASE
WHEN SUM(amount) >= 100000 THEN 'VIP'
WHEN SUM(amount) >= 10000 THEN 'Loyal'
WHEN SUM(amount) >= 1000 THEN 'Regular'
ELSE 'New'
END AS segment
FROM orders
GROUP BY user_id;
Какие 5 частых паттернов CASE WHEN?
Конверсия в boolean
CASE WHEN status = 'paid' THEN 1 ELSE 0 END AS is_paid
Можно потом SUM(is_paid) чтобы посчитать количество оплат.
Pivot (горизонтальный разворот)
SELECT
DATE(created_at) AS day,
SUM(CASE WHEN country = 'RU' THEN amount END) AS revenue_ru,
SUM(CASE WHEN country = 'KZ' THEN amount END) AS revenue_kz
FROM orders
GROUP BY day;
Категоризация (как в примере выше)
Замена пропусков (альтернатива COALESCE)
CASE WHEN col IS NULL THEN 'unknown' ELSE col END
-- эквивалентно COALESCE(col, 'unknown')
Конверсия для расчёта метрик
-- Конверсия в проценты
SUM(CASE WHEN paid THEN 1 ELSE 0 END) * 100.0 / COUNT(*) AS conversion_pct
Типичная метрика через CASE: «конверсия в оплату по каналам». SUM(CASE WHEN paid THEN 1 ELSE 0 END) * 100.0 / COUNT(*) AS conversion_pct GROUP BY channel.
Какие 5 ошибок с NULL и типами?
- Ошибка 1:
WHERE col = NULL→ возвращает 0. ИспользуйIS NULL. - Ошибка 2:
COUNT(col)ожидая всех строк → только не-NULL. ИспользуйCOUNT(*). - Ошибка 3:
a / bгде b может быть 0 или NULL → div-by-zero или NULL. ИспользуйNULLIF(b, 0)+ COALESCE. - Ошибка 4:
'1' + 1в MySQL = 2 (silent cast), в PG — error. Лучше явный CAST. - Ошибка 5:
CASE WHEN col = 'value'для NULL — никогда не сработает. ДобавьWHEN col IS NULL.
Частые вопросы про NULL и типы
NULL — это false?
В булевом контексте NULL ведёт себя как unknown, не false. WHERE NULL — пропускает строку.
Как INTEGER отличается от BIGINT?
INTEGER — 32 бита (до 2.1 миллиарда). BIGINT — 64 бита (до 9.2 × 10^18). Для id юзеров на большом сервисе — BIGINT.
NUMERIC или DECIMAL — что брать?
Эквивалентны. NUMERIC — стандарт ANSI, DECIMAL — синоним.
Можно ли default value на колонке вместо COALESCE?
Да, на уровне CREATE TABLE: amount NUMERIC DEFAULT 0. Но это для INSERT. SELECT уже отдаст значение или NULL.
CASE WHEN внутри агрегата — нормально?
Да, очень частый паттерн (см. конверсия). SUM(CASE WHEN paid THEN 1 ELSE 0 END) или короче COUNT(*) FILTER (WHERE paid) (PG-specific).
Что дальше?
В Части 8 — работа с датой и временем. DATE_TRUNC для группировки по месяцам, EXTRACT для извлечения года/месяца, INTERVAL для расчётов.
В Pro — безлимит мок-собесов на AI-интервью + 491 SQL-задача + 612 тестовых заданий + 50+ блог-постов.
Навигация по учебнику
← Часть 6 | Часть 7: NULL, типы, CASE | Часть 8 →
Содержание серии: 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8 · 9 · 10
Источники
- PostgreSQL Docs: «Data Types» (postgresql.org/docs/current/datatype.html)
- PostgreSQL Docs: «Conditional Expressions» (postgresql.org/docs/current/functions-conditional.html)