Диалект — PostgreSQL. Таблицы, запросы, WHERE, ORDER BY, LIMIT.
Реляционная база данных — это набор таблиц, связанных между собой.
Таблица = сущность (например, «сотрудники»). Строка (запись) = один объект (один сотрудник). Столбец (поле) = характеристика (имя, зарплата). SQL = язык, на котором мы просим базу эти данные найти, посчитать или изменить.
employees| id | name | department | city | salary | bonus |
|---|---|---|---|---|---|
| 1 | Анна | IT | Москва | 120000 | 20000 |
| 2 | Иван | Продажи | СПб | 80000 | NULL |
| 3 | Мария | IT | Казань | 130000 | 25000 |
| 4 | Андрей | Маркетинг | Москва | 70000 | NULL |
| 5 | Елена | Продажи | СПб | 90000 | 10000 |
| 6 | Павел | IT | Москва | 110000 | NULL |
| 7 | Дарья | Маркетинг | Казань | 75000 | 5000 |
| 8 | Сергей | Продажи | Москва | 85000 | NULL |
Обрати внимание на
NULLвbonus. Это не ноль и не пустая строка, а «значения нет». С ним мы разберёмся отдельно — это частая причина ошибок.
SELECT, FROM, WHERE) пишут заглавными — это просто договорённость для читаемости.;. Строковые значения берём в одинарные кавычки: 'IT'. Двойные кавычки — для имён таблиц и столбцов.FROM — откуда берём, SELECT — что берём.
SELECT name, salary
FROM employees;Результат — 8 строк с двумя столбцами.
SELECT *
FROM employees;* удобно для быстрого просмотра, но в реальных проектах лучше перечислять поля явно: так запрос понятнее и не тащит лишние данные.
ASМожно переименовать столбец в результате (не в самой таблице!):
SELECT name AS employee_name, salary AS monthly_salary
FROM employees;Слово AS необязательное: salary monthly_salary работает так же.
💡 Важный нюанс PostgreSQL: псевдоним нельзя использовать в
WHERE:-- Так НЕ работает: SELECT salary AS pay FROM employees WHERE pay > 100000;Причина станет понятна в разделе про порядок выполнения.
Посмотрим, в каких городах работают сотрудники:
SELECT city
FROM employees;Получим: Москва, СПб, Казань, Москва, СПб, Москва, Казань, Москва.
SELECT DISTINCT city
FROM employees;Получим: Москва, СПб, Казань — по одному разу.
DISTINCT действует по всей комбинации выбранных полей:
SELECT DISTINCT department, city
FROM employees;Здесь дубль уберётся только если совпадают и отдел, и город одновременно.
WHERE фильтрует строки до того, как они попадут в результат. Условие должно быть истинным (TRUE).
SELECT name, salary
FROM employees
WHERE salary > 100000;→ Анна (120 000), Мария (130 000), Павел (110 000).
Операторы сравнения: =, <> (или !=), >, <, >=, <=.
⚠️ Одно
=— это сравнение, а не присваивание.
И то, и другое (AND):
SELECT name, salary
FROM employees
WHERE department = 'IT'
AND salary >= 110000;→ Анна, Мария, Павел.
Хотя бы одно (OR):
SELECT name, city
FROM employees
WHERE city = 'Казань'
OR salary > 100000;→ Анна, Мария, Павел, Дарья.
Инверсия (NOT):
SELECT name
FROM employees
WHERE NOT department = 'IT';→ все, кроме сотрудников IT.
AND имеет больший приоритет, чем OR. Без скобок легко получить не то:
-- Непонятно и рискованно:
WHERE department = 'IT' OR department = 'Маркетинг' AND city = 'Москва'
-- Явно и правильно:
WHERE (department = 'IT' OR department = 'Маркетинг')
AND city = 'Москва';Второй вариант: Анна, Павел, Андрей. Всегда используй скобки, когда мешаешь AND и OR.
Это сокращённые и более удобные формы условий.
Вместо city = 'Москва' OR city = 'Казань':
SELECT name, city
FROM employees
WHERE city IN ('Москва', 'Казань');Работает и наоборот:
WHERE department NOT IN ('IT', 'Продажи')→ только Маркетинг.
SELECT name, salary
FROM employees
WHERE salary BETWEEN 80000 AND 100000;→ Иван (80 000), Елена (90 000), Сергей (85 000).
Это то же самое, что salary >= 80000 AND salary <= 100000. Работает не только с числами, но и с датами.
Два спецсимвола:
% — любое количество символов, в том числе ноль;_ — ровно один символ.-- Имена, начинающиеся на «А»
SELECT name FROM employees WHERE name LIKE 'А%';→ Анна, Андрей.
-- Третья и четвёртая буквы — «ар»
SELECT name FROM employees WHERE name LIKE '_ар%';→ Мария, Дарья.
В PostgreSQL LIKE чувствителен к регистру. Для поиска без учёта регистра есть ILIKE:
WHERE name ILIKE 'а%';Главная ловушка этапа: сравнивать с NULL через = нельзя.
-- НЕ РАБОТАЕТ (вернёт ноль строк):
WHERE bonus = NULL;
-- ПРАВИЛЬНО:
SELECT name FROM employees WHERE bonus IS NULL;→ Иван, Андрей, Павел, Сергей.
А наличие премии:
SELECT name, bonus
FROM employees
WHERE bonus IS NOT NULL;→ Анна, Мария, Елена, Дарья.
Почему так:
NULL— это «неизвестно», а сравнение с неизвестным даёт не истину, аUNKNOWN, и такая строка в результат не попадает. То же касаетсяNOT INсо списком, содержащимNULL, — запомни на будущее.
Без ORDER BY порядок строк не гарантирован. Даже если кажется, что они идут по id, не рассчитывай на это.
-- По зарплате от большой к маленькой
SELECT name, salary
FROM employees
ORDER BY salary DESC;ASC — по возрастанию (по умолчанию);DESC — по убыванию.SELECT department, name, salary
FROM employees
ORDER BY department ASC, salary DESC;Сначала группируем визуально по отделам (буквенный порядок), внутри отдела — зарплата по убыванию.
Сортировать можно и по полю, которого нет в SELECT: SELECT name FROM employees ORDER BY salary DESC; — это нормально.
NULLВ PostgreSQL при ASC они уходят в конец, при DESC — в начало. Управлять этим можно явно:
ORDER BY bonus DESC NULLS LAST;SELECT name, salary
FROM employees
ORDER BY salary DESC
LIMIT 3;→ Мария (130 000), Анна (120 000), Павел (110 000).
⚠️ Без
ORDER BYLIMIT 3вернёт «какие-то три строки», а не «три самые большие».LIMITпочти всегда пишется вместе сORDER BY.
OFFSETSELECT name, salary
FROM employees
ORDER BY salary DESC
LIMIT 2 OFFSET 2;Пропускаем первых двух, берём следующих двух → Елена (90 000), Сергей (85 000).
Логика: OFFSET — сколько пропустить, LIMIT — сколько взять.
Мы пишем SELECT первым, но база выполняет запрос не в том порядке:
| Этап | Клауза | Описание |
|---|---|---|
| 1 | FROM | → берём таблицу |
| 2 | WHERE | → отбрасываем лишние строки |
| 3 | GROUP BY | → группируем |
| 4 | HAVING | → фильтруем группы |
| 5 | SELECT | → считаем/выбираем столбцы, применяем DISTINCT |
| 6 | ORDER BY | → сортируем готовый результат |
| 7 | LIMIT | → обрезаем |
Отсюда три следствия, которые снимут 90% твоих ошибок:
WHERE нельзя использовать псевдоним из SELECT — его ещё не существует.WHERE нельзя использовать агрегаты вроде SUM() — для этого будет HAVING.ORDER BY работает уже с результатом, поэтому там псевдонимы как раз разрешены.Попробуй решить самостоятельно, не подглядывая ниже.
IN).NULL).-- 1
SELECT name, department, salary
FROM employees;
-- 2
SELECT DISTINCT department
FROM employees;
-- 3
SELECT name, salary
FROM employees
WHERE city = 'Москва' AND salary > 85000;
-- Анна, Павел
-- 4
SELECT name, department
FROM employees
WHERE department IN ('IT', 'Маркетинг');
-- 5
SELECT name, salary
FROM employees
WHERE salary BETWEEN 75000 AND 90000;
-- Иван, Елена, Дарья, Сергей
-- 6
SELECT name
FROM employees
WHERE bonus IS NULL;
-- 7
SELECT name, salary
FROM employees
ORDER BY salary ASC
LIMIT 3;
-- Андрей 70000, Дарья 75000, Иван 80000
-- 8
SELECT name, salary, bonus
FROM employees
WHERE (salary > 100000 OR city = 'Казань')
AND bonus IS NOT NULL;
-- Анна, МарияТы готова ко второму этапу, если можешь без подсказки объяснить:
WHERE отличается от ORDER BY по моменту выполнения;bonus = NULL не работает;LIKE '%а%' отличается от LIKE '_а%';LIMIT нужна парочка с ORDER BY;AND и OR.Хочешь, я дам тебе ещё 5 задач с чуть более хитными условиями (на OR, NOT IN и LIKE), а ты пришлёшь свои запросы — я проверю и разберу ошибки?