SELECT читает данные из таблицы: вы перечисляете нужные столбцы, после FROM пишете имя таблицы, а условия, сортировку и группировку добавляете по мере надобности. Самый короткий запрос к таблице в MySQL выглядит так: SELECT * FROM customers;. Он вернёт все строки и все столбцы таблицы customers.
Ниже разберём синтаксис команды select по частям: выбор столбцов, фильтр WHERE, сортировку ORDER BY и LIMIT, группировку GROUP BY с HAVING и выборку из двух таблиц через JOIN. Если SQL для вас пока незнакомое слово, начните с обзора языка SQL и того, зачем его учить, а сюда возвращайтесь с первыми запросами.
Все примеры мы запустили на MySQL 8.0.46 с настройками по умолчанию и вставили в статью реальный вывод. В MySQL 8.4 синтаксис SELECT тот же, справочник по нему лежит в официальной документации MySQL (из России этот сайт сейчас часто открывается страницей с ошибкой, поэтому все правила в статье мы проверили запросами). Подборка курсов, где SQL учат с нуля на задачах, есть в нашем каталоге курсов SQL.
Синтаксис SELECT в MySQL и порядок частей запроса

Полный оператор select длинный, но в работе нужны семь частей. Порядок, в котором их пишут, фиксирован: документация MySQL прямо требует ставить HAVING после GROUP BY и перед ORDER BY.
SELECT столбцы или выражения
FROM таблица
WHERE условие для строк
GROUP BY столбцы для группировки
HAVING условие для групп
ORDER BY столбцы для сортировки
LIMIT сколько строк вернуть;
Обязателен только SELECT. Остальные части добавляются, когда нужны: простой запрос часто состоит из SELECT, FROM и WHERE.
Пишут запрос в одном порядке, а логически база обрабатывает его в другом. Из-за этой разницы новички чаще всего и ошибаются, поэтому сохраните таблицу.
| Шаг | Часть запроса | Что делает |
|---|---|---|
| 1 | FROM, JOIN | Берёт таблицы и соединяет их |
| 2 | WHERE | Отбрасывает строки, которые не подходят под условие |
| 3 | GROUP BY | Собирает оставшиеся строки в группы |
| 4 | HAVING | Отбрасывает группы |
| 5 | SELECT | Считает выражения и присваивает псевдонимы |
| 6 | ORDER BY | Сортирует результат |
| 7 | LIMIT | Отрезает нужное число строк |
Отсюда правило: псевдоним из SELECT нельзя использовать в WHERE, потому что на шаге 2 его ещё не существует. В ORDER BY, GROUP BY и HAVING псевдоним работать будет, это тоже написано в разделе документации о псевдонимах.
Учебные таблицы для примеров
Чтобы повторить запросы у себя, создайте две таблицы: клиентов и заказов. Подойдёт любая база MySQL, хоть локальная, хоть в облаке. Запросы можно выполнять в консольном клиенте mysql, в MySQL Workbench или в DBeaver, по нему у нас есть разбор подключения DBeaver к базе и частых ошибок.
CREATE TABLE customers (
id INT PRIMARY KEY,
name VARCHAR(50),
city VARCHAR(50)
);
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT,
product VARCHAR(50),
amount DECIMAL(10,2),
created_at DATE,
status VARCHAR(20)
);
INSERT INTO customers VALUES
(1, 'Анна', 'Москва'),
(2, 'Борис', 'Казань'),
(3, 'Вера', 'Москва'),
(4, 'Глеб', NULL);
INSERT INTO orders VALUES
(101, 1, 'Ноутбук', 85000, '2026-09-02', 'paid'),
(102, 2, 'Мышь', 1500, '2026-09-05', 'paid'),
(103, 1, 'Монитор', 23000, '2026-09-11', 'cancelled'),
(104, 3, 'Клавиатура', 4200, '2026-09-18', 'paid'),
(105, 2, 'Наушники', 7900, '2026-10-01', 'new'),
(106, 3, 'Ноутбук', 91000, '2026-10-03', 'paid');
У Глеба город не указан, поле хранит NULL. Это сделано нарочно: с NULL чаще всего ошибаются, и ниже это видно на живом примере.
Канал основателя Checkroi Вани Буявца3 700 человек читают мой Телеграм-канал про нейросетиСобрал промпты для Claude Code и ChatGPT, разборы ИИ-инструментов и лайфхаки по продвижению бизнеса в одном местеПрисоединитьсяКак выбрать столбцы: SELECT *, список полей и псевдонимы
Звёздочка возвращает все столбцы в том порядке, в каком они объявлены в таблице.
SELECT * FROM customers;
+----+-------+--------+
| id | name | city |
+----+-------+--------+
| 1 | Анна | Москва |
| 2 | Борис | Казань |
| 3 | Вера | Москва |
| 4 | Глеб | NULL |
+----+-------+--------+
В рабочих запросах лучше перечислять столбцы явно. Так база не тянет лишние данные, а код не ломается, когда в таблицу добавят новое поле. Это одно из первых правил, которое слышит начинающий SQL-разработчик на код-ревью.
SELECT product, amount FROM orders;
Псевдонимы и вычисления
В списке SELECT можно считать: умножать, склеивать строки, вызывать функции. Ключевое слово AS даёт столбцу понятное имя.
SELECT product AS товар, amount * 0.8 AS со_скидкой
FROM orders
WHERE id = 101;
+---------+------------+
| товар | со_скидкой |
+---------+------------+
| Ноутбук | 68000.000 |
+---------+------------+
Формально AS можно не писать, но справочник по SELECT советует ставить его всегда. Без него забытая запятая между двумя столбцами молча превращает второй столбец в псевдоним первого, и запрос возвращает не то, что вы ждали.
DISTINCT и SELECT без таблицы
DISTINCT убирает повторы. NULL при этом считается отдельным значением и попадает в результат:
SELECT DISTINCT city FROM customers;
+--------+
| city |
+--------+
| Москва |
| Казань |
| NULL |
+--------+
А ещё SELECT умеет работать вообще без FROM. Запрос SELECT 1 часто используют, чтобы проверить, что соединение с базой живо:
SELECT 1, 2 + 2 AS sum;
+---+-----+
| 1 | sum |
+---+-----+
| 1 | 4 |
+---+-----+
Фильтрация строк через WHERE
WHERE оставляет только строки, для которых условие истинно. Условия соединяют через AND и OR, а скобки задают, что проверять первым. Из таких фильтров состоит большая часть рабочего дня SQL-аналитика: выгрузить заказы за период, отобрать клиентов из одного города, найти пустые поля.
SELECT id, product, amount
FROM orders
WHERE status = 'paid' AND amount > 5000;
+-----+---------+----------+
| id | product | amount |
+-----+---------+----------+
| 101 | Ноутбук | 85000.00 |
| 106 | Ноутбук | 91000.00 |
+-----+---------+----------+
Кроме обычных сравнений, в WHERE постоянно встречаются ещё четыре оператора:
| Оператор | Пример | Что вернул на наших данных |
|---|---|---|
| IN | status IN ('new', 'cancelled') |
Заказы 103 и 105 |
| BETWEEN | created_at BETWEEN '2026-09-01' AND '2026-09-30' |
Четыре сентябрьских заказа, 101–104; границы входят в диапазон |
| LIKE | name LIKE 'В%' |
Вера: % означает любое число любых символов |
| IS NULL | city IS NULL |
Глеб |
Почему city = NULL ничего не находит
Запрос SELECT name FROM customers WHERE city = NULL; вернёт пустой результат, хотя Глеб в таблице есть. Любое сравнение с NULL через =, < или <> даёт NULL, а не «истину», поэтому строка не проходит фильтр.
Решение: проверять пустое значение только через IS NULL и IS NOT NULL. Так написано и в разделе документации о работе с NULL.
Тот же эффект бывает с отрицанием. Мы ждали, что WHERE city <> 'Москва' вернёт Бориса и Глеба, а получили только Бориса: строка с NULL снова выпала. Если она нужна, добавьте OR city IS NULL, а остальные подвохи пустых значений собраны в разборе NULL в SQL.
Сортировка ORDER BY и ограничение LIMIT
ORDER BY сортирует по одному или нескольким столбцам, DESC разворачивает порядок. LIMIT отрезает первые строки результата, и вместе они дают классический «топ».
SELECT id, product, amount
FROM orders
ORDER BY amount DESC
LIMIT 3;
+-----+---------+----------+
| id | product | amount |
+-----+---------+----------+
| 106 | Ноутбук | 91000.00 |
| 101 | Ноутбук | 85000.00 |
| 103 | Монитор | 23000.00 |
+-----+---------+----------+
Для постраничного вывода добавляют OFFSET: сколько строк пропустить. Запрос ниже показывает вторую «страницу» по две строки.
SELECT id, product, amount
FROM orders
ORDER BY amount DESC
LIMIT 2 OFFSET 2;
+-----+----------+----------+
| id | product | amount |
+-----+----------+----------+
| 103 | Монитор | 23000.00 |
| 105 | Наушники | 7900.00 |
+-----+----------+----------+
MySQL понимает и короткую запись LIMIT 2, 2: первое число означает смещение, второе количество строк. Вариант с OFFSET читается проще и работает так же в PostgreSQL.
LIMIT без ORDER BY не гарантирует, какие строки вы получите. Даже с ORDER BY строки с одинаковым значением сортировки MySQL вправе отдавать в любом порядке. Чтобы страницы не «прыгали», добавьте в сортировку уникальный столбец:
ORDER BY amount DESC, id. Этот совет есть в описании оптимизации LIMIT.
Группировка GROUP BY, агрегатные функции и HAVING

GROUP BY собирает строки с одинаковым значением в одну группу, а агрегатные функции считают по группе итог: COUNT считает строки, SUM складывает, AVG даёт среднее, MIN и MAX находят крайние значения.
SELECT status, COUNT(*) AS заказов, SUM(amount) AS сумма
FROM orders
GROUP BY status
ORDER BY сумма DESC;
+-----------+---------+-----------+
| status | заказов | сумма |
+-----------+---------+-----------+
| paid | 4 | 181700.00 |
| cancelled | 1 | 23000.00 |
| new | 1 | 7900.00 |
+-----------+---------+-----------+
Такие сводки по статусам и периодам потом превращаются в графики: этим занимается BI-аналитик, и GROUP BY у него в каждом втором запросе.
Обратите внимание на COUNT. COUNT(*) считает все строки, а COUNT(city) только те, где город заполнен, так работает функция COUNT по описанию в справочнике. На таблице клиентов первый вернул 4, второй 3.
Чем WHERE отличается от HAVING
WHERE фильтрует строки до группировки, HAVING фильтрует уже готовые группы. Запрос ниже сначала оставляет оплаченные заказы, потом складывает их по клиентам и показывает тех, кто заплатил больше 50 000 ₽.
SELECT customer_id, SUM(amount) AS оплачено
FROM orders
WHERE status = 'paid'
GROUP BY customer_id
HAVING SUM(amount) > 50000;
+-------------+----------+
| customer_id | оплачено |
+-------------+----------+
| 1 | 85000.00 |
| 3 | 95200.00 |
+-------------+----------+
Условие на сумму в WHERE поставить нельзя: на этом шаге групп ещё нет. Разницу между WHERE и HAVING регулярно спрашивают на собеседованиях, ещё три типовые задачи разобраны в статье про собеседование на аналитика данных.
SELECT из нескольких таблиц: JOIN и подзапрос

Данные о клиенте и о заказе лежат в разных таблицах, а в отчёте их нужно увидеть рядом. Для этого таблицы соединяют по общему полю: у нас это orders.customer_id и customers.id.
SELECT c.name, o.product, o.amount
FROM orders AS o
INNER JOIN customers AS c ON c.id = o.customer_id
WHERE o.status = 'paid'
ORDER BY o.amount DESC;
+-------+------------+----------+
| name | product | amount |
+-------+------------+----------+
| Вера | Ноутбук | 91000.00 |
| Анна | Ноутбук | 85000.00 |
| Вера | Клавиатура | 4200.00 |
| Борис | Мышь | 1500.00 |
+-------+------------+----------+
Короткие имена o и c здесь псевдонимы таблиц. Без них пришлось бы каждый раз писать полное имя, а в запросе с тремя-четырьмя таблицами это быстро становится нечитаемым.
INNER JOIN и LEFT JOIN
INNER JOIN оставляет только пары, у которых нашлось совпадение. LEFT JOIN сохраняет все строки левой таблицы, даже если справа для них ничего нет. Так видно клиентов без заказов:
SELECT c.name, COUNT(o.id) AS заказов
FROM customers AS c
LEFT JOIN orders AS o ON o.customer_id = c.id
GROUP BY c.id, c.name
ORDER BY c.id;
+-------+---------+
| name | заказов |
+-------+---------+
| Анна | 2 |
| Борис | 2 |
| Вера | 2 |
| Глеб | 0 |
+-------+---------+
С INNER JOIN Глеб из этого отчёта просто исчез бы. Поэтому считаем COUNT(o.id), а не COUNT(*): у Глеба строка есть, но поля заказа в ней пустые, и звёздочка показала бы единицу.
Подзапрос в WHERE
Иногда проще не соединять таблицы, а подставить результат одного SELECT в другой. Соединения и подзапросы подробно разбирают на курсах по MySQL, здесь покажем базовый вариант. Найдём клиентов, у которых был заказ дороже 50 000 ₽:
SELECT name
FROM customers
WHERE id IN (SELECT customer_id FROM orders WHERE amount > 50000);
+------+
| name |
+------+
| Анна |
| Вера |
+------+
Частые ошибки в запросах SELECT и что с ними делать
Все ошибки ниже мы получили сами на тех же таблицах, текст сообщений скопирован из вывода MySQL.
- ERROR 1055 … incompatible with sql_mode=only_full_group_by. Причина: в SELECT есть столбец, которого нет в GROUP BY и который не посчитан агрегатной функцией. Например,
SELECT customer_id, product, SUM(amount) FROM orders GROUP BY customer_id: у одного клиента несколько товаров, и базе непонятно, какой показать. Решение: добавить столбец в GROUP BY, обернуть его в агрегат или, если значение в группе точно одно, вANY_VALUE(). Режим включён по умолчанию, подробности в разделе о GROUP BY. - ERROR 1054 Unknown column ‘со_скидкой’ in ‘where clause’. Причина: псевдоним из SELECT использован в WHERE. Решение: повторить в условии само выражение,
WHERE amount * 0.8 > 1000, или перенести фильтр в HAVING. - Пустой результат на city = NULL. Причина разобрана выше. Решение:
IS NULL, а в условиях с<>помнить, что NULL-строки выпадут. - ERROR 1054 Unknown column ‘Мышь’. Причина: строка в двойных кавычках, а на сервере включён режим ANSI_QUOTES, в котором двойные кавычки означают имя столбца. С настройками по умолчанию MySQL принимает обе кавычки, поэтому запрос может работать у вас и ломаться на другом сервере. Решение: строки всегда в одинарных кавычках, это правило строковых литералов MySQL.
- Каждый раз разные строки в выдаче с LIMIT. Причина: нет ORDER BY или сортировка по неуникальному полю. Решение: добавить в конец сортировки id.
Как тренироваться писать запросы SELECT
Синтаксис SELECT можно выучить за вечер. Писать запросы без подсказок получается позже, когда вы решите десятки задач на своих данных. Ускорить это помогут три привычки.
- Своя учебная база. Таблицы из этой статьи создаются за минуту. Добавьте третью, например товары, и попробуйте ответить на вопросы бизнеса: кто из клиентов вернулся повторно, какой товар отменяют чаще остальных.
- Сначала строки, потом итоги. Пишите запрос по шагам: SELECT * с WHERE, проверили строки, затем GROUP BY. Ошибку в данных на маленьком результате видно сразу.
- План запроса. Когда запросы станут тяжелее, допишите перед SELECT слово EXPLAIN: MySQL покажет, какие индексы он использует.
Если SQL нужен для смены профессии, посмотрите, как SELECT вписывается в работу целиком, в нашем плане как стать SQL-разработчиком.
Где освоить SQL и MySQL системно
Для повседневных выгрузок одного SELECT хватает. Дальше начинаются индексы, оконные функции, транзакции и оптимизация. Разбирать их по отдельным статьям долго, удобнее пройти курс с практикой и проверкой заданий.
В каталоге есть курсы по SQL от разных школ с ценами и отзывами, среди них программы с упором на MySQL. Вот курсы с самым высоким рейтингом:
| Курс | Школа | Стоимость со скидкой | В рассрочку | Длительность | Обзор курса от Checkroi |
|---|---|---|---|---|---|
| Нейросети для рабочих задач Перейти на сайт курса | 31 290 ₽ | - | 1 месяц | Обзор курса | |
| SQL-разработчик: тариф PRO Перейти на сайт курса | 89 900 ₽ | 7491 ₽/мес. | 2 месяца | Обзор курса | |
| SQL-разработчик: тариф Базовый Перейти на сайт курса | 79 900 ₽ | 3329 ₽/мес. | 3 месяца | Обзор курса | |
| SQL для анализа данных Перейти на сайт курса | 50 828 ₽ | 6203 ₽/мес. | 2 месяца | Обзор курса | |
| SQL с нуля | 134 640 ₽ | 5500 ₽/мес. | 12 месяцев | Обзор курса |
Больше программ — в полном каталоге курсов по SQL




