SQL-запрос SELECT: как выбирать данные из таблиц
Оператор SELECT в SQL извлекает данные из таблиц базы: отдельные столбцы, все поля, вычисленные значения или итоги. В статье разобраны синтаксис и порядок частей запроса, псевдонимы, удаление повторов, ограничение числа строк, выражения и условия CASE, а также чтение результата выборки в программе на Python.

Содержание статьи
Синтаксис и порядок частей запроса
Минимальный запрос состоит из двух частей: после SELECT перечисляются столбцы, после FROM указывается таблица. Остальные части необязательны, но если они есть, их пишут строго в определенном порядке.
SELECT [DISTINCT] столбцы
FROM таблица
[WHERE условие на строки]
[GROUP BY столбцы группировки]
[HAVING условие на группы]
[ORDER BY столбцы сортировки]
[LIMIT число];Выполняется запрос в другом порядке, чем пишется: сначала FROM, затем WHERE, GROUP BY, HAVING, потом вычисляется список SELECT, и только после этого ORDER BY и LIMIT. Поэтому псевдоним столбца, объявленный в SELECT, можно использовать в ORDER BY, но нельзя в WHERE.
Примеры ниже выполняются над таблицей сотрудников:
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
name TEXT, dept TEXT, salary INTEGER, email TEXT
);
INSERT INTO employees (name, dept, salary, email) VALUES
('Иван', 'sales', 90000, 'ivan@corp.ru'),
('Мария', 'it', 150000, NULL),
('Петр', 'sales', 70000, 'petr@corp.ru'),
('Ольга', 'hr', 80000, 'olga@corp.ru');Выбор столбцов и всех полей
Нужные столбцы перечисляются через запятую в любом порядке, именно в этом порядке они придут в результате. Звездочка выбирает все столбцы таблицы.
SELECT name, salary FROM employees;
-- Иван | 90000
-- Мария | 150000
-- ...
SELECT * FROM employees;Звездочка удобна для просмотра таблицы, но в коде приложений ее избегают. Если в таблицу добавят столбец, результат запроса изменится, а лишние поля, особенно большие текстовые, увеличивают объем передаваемых данных. В программах лучше явно перечислять нужные столбцы.
Псевдонимы через AS
Ключевое слово AS дает столбцу или таблице другое имя в результате. Это нужно для вычисляемых полей, которые иначе получат имя вроде salary*12, и для коротких имен таблиц в запросах с соединениями.
SELECT e.name AS сотрудник, e.salary * 12 AS год
FROM employees AS e
ORDER BY год DESC;Если в псевдониме есть пробел, его заключают в двойные кавычки: AS "годовой доход". Слово AS перед псевдонимом можно опустить, но с ним запрос читается легче.
Вычисления и условия в списке столбцов
В SELECT можно писать не только имена столбцов, но и выражения: арифметику, функции, константы. Значение вычисляется для каждой строки, исходные данные в таблице не меняются.
SELECT name,
UPPER(dept) AS отдел,
salary * 0.87 AS на_руки,
COALESCE(email, 'нет почты') AS почта,
CASE WHEN salary >= 100000 THEN 'высокая'
ELSE 'обычная' END AS уровень
FROM employees;Конструкция CASE работает как цепочка if/elif/else: проверяет условия по порядку и возвращает первое подходящее значение. COALESCE() подставляет замену вместо NULL. Строки склеиваются оператором || в SQLite и PostgreSQL или функцией CONCAT() в MySQL.
Уникальные строки и ограничение выборки
DISTINCT сразу после SELECT убирает из результата одинаковые строки. Уникальность проверяется по всем выбранным столбцам вместе, а не по первому.
SELECT DISTINCT dept FROM employees; -- sales, it, hr
SELECT name, salary FROM employees
ORDER BY salary DESC
LIMIT 2 OFFSET 1; -- 2-е и 3-е место по зарплатеLIMIT ограничивает число строк, OFFSET пропускает указанное количество строк с начала. Без ORDER BY порядок строк не определен, поэтому LIMIT почти всегда идет вместе с сортировкой. Синтаксис зависит от СУБД:
| СУБД | Первые 10 строк |
|---|---|
| SQLite, PostgreSQL, MySQL | ... LIMIT 10 |
| SQL Server | SELECT TOP 10 ... |
| Oracle 12c+, PostgreSQL | ... FETCH FIRST 10 ROWS ONLY |
Фильтрация, сортировка и итоги
SELECT почти никогда не используется в одиночку. Условие на строки задается в WHERE, порядок вывода в ORDER BY, а итоги считаются агрегатными функциями.
SELECT name, salary
FROM employees
WHERE dept = 'sales' AND salary > 60000
ORDER BY salary DESC;
SELECT dept, COUNT(*) AS людей, AVG(salary) AS средняя
FROM employees
GROUP BY dept;Подробнее о сортировке по нескольким столбцам написано в статье про ORDER BY, а о COUNT, SUM и AVG в разборе агрегатных функций. Данные из нескольких связанных таблиц объединяют оператором JOIN, а результат одного SELECT можно использовать внутри другого как подзапрос: WHERE salary > (SELECT AVG(salary) FROM employees).
Выполнение SELECT из Python
В модуле sqlite3 запрос выполняется методом execute(), а строки результата забираются через fetchone(), fetchall() или перебором курсора в цикле. По умолчанию каждая строка приходит кортежем. Если задать row_factory = sqlite3.Row, к полям можно обращаться по имени столбца.
import sqlite3
conn = sqlite3.connect("company.db")
conn.row_factory = sqlite3.Row
rows = conn.execute(
"SELECT name, salary FROM employees WHERE dept = ? ORDER BY salary DESC",
("sales",),
).fetchall()
for row in rows:
print(row["name"], row["salary"])
# Иван 90000
# Петр 70000
conn.close()Значения в запрос передаются через плейсхолдер ?, а не форматированием строки. Так драйвер сам экранирует данные, и запрос защищен от SQL-инъекций. Для больших таблиц не вызывайте fetchall(): перебор курсора в цикле читает строки постепенно и не загружает всю выборку в память.
Частые вопросы
Ответы на типичные вопросы о запросе SELECT.
Можно ли выполнить SELECT без FROM?
Да, в SQLite, PostgreSQL и MySQL запрос SELECT 2 + 2; или SELECT CURRENT_DATE; вернет одно вычисленное значение. В Oracle для этого используют служебную таблицу DUAL.
Почему нельзя использовать псевдоним в WHERE?
WHERE выполняется раньше, чем вычисляется список SELECT, и псевдонима в этот момент еще нет. Повторите выражение в условии или оберните запрос в подзапрос.
Чем DISTINCT отличается от GROUP BY?
Для простого удаления повторов результат одинаковый. GROUP BY нужен, когда по каждой группе считаются итоги через COUNT, SUM и другие функции.
Как выбрать случайные строки?
В SQLite и PostgreSQL используют ORDER BY RANDOM() LIMIT 5, в MySQL ORDER BY RAND(). На больших таблицах такой запрос медленный, потому что сортирует всю таблицу.