Основы языка SQL

Оператор SELECT. Часть 1

В материале рассматривается компонент DML языка SQL, отвечающий за манипулирование данными. Основной фокус направлен на детальный разбор оператора SELECT: его синтаксиса, логики фильтрации (WHERE, HAVING), группировки (GROUP BY) и сортировки (ORDER BY). Объясняется, как соединять несколько таблиц в одном запросе, использовать агрегатные функции и подзапросы. Теоретической базой выступает реляционная алгебра, однако акцент сделан на практическом применении команд для извлечения, фильтрации и агрегации данных.

Основные мысли

В результате изучения лекции слушатель будет способен:
1. Объяснить разницу между компонентами DDL и DML языка SQL и их роль в работе с базами данных.
2. Описать синтаксис оператора SELECT и строгий порядок следования его секций.
3. Составлять запросы для фильтрации данных с использованием секции WHERE, включая операторы LIKE, BETWEEN, IN и логические связки.
4. Демонстрировать умение соединять несколько таблиц в одном запросе, явно указывая условия склейки.
5. Применять агрегатные функции (COUNT, SUM, AVG, MAX, MIN) для вычисления итоговых значений по столбцам.
6. Формулировать запросы с группировкой данных, используя секцию GROUP BY, и корректно определять поля для группировки.
7. Отличать логику работы и область применения секций фильтрации WHERE и HAVING.
8. Конструировать запросы с сортировкой результатов по одному или нескольким полям с помощью ORDER BY.
9. Использовать псевдонимы (alias) для переименования столбцов в результирующей выборке и опцию DISTINCT для исключения дубликатов.
Показывать лекцию целиком
Краткое изложение

Мы переходим к компоненте языка SQL под названием DML — Data Manipulation Language (язык манипулирования данными). Ключевые запросы этой компоненты — SELECT, INSERT, DELETE и UPDATE, то есть выборка, вставка, удаление и обновление данных.

Когда говорят о языке SQL, чаще всего имеют в виду именно его DML-компоненту. DDL, рассмотренный ранее, на уровне промышленных проектов обычно существует в виде готовых скриптов, которые передаются заказчику. Сам же DDL-код часто генерируется автоматически различными клиентами баз данных, где можно просто «прокликать» нужные действия мышью. Тем не менее, понимание DDL-команд является важной составляющей работы. Однако большая часть команд, которые вы будете писать, относится к DML.

Оператор SELECT и его структура

Первый и основной оператор, который мы рассмотрим, — SELECT. Это оператор извлечения записей из базы данных.

В основе языка SQL лежит глубокая концепция — реляционная алгебра. Она определяет базовые операции (такие как объединение, вычитание, проекция, выборка, соединение), с помощью которых можно решить любую задачу по извлечению данных. Задача SQL как раз и состояла в том, чтобы реализовать эти операции. Мы не будем погружаться в математический фундамент, а сосредоточимся на конечном инструменте — языке запросов.

Синтаксис оператора SELECT фиксирован. Порядок следования его секций строгий:
1. SELECT — список столбцов, которые нужно вернуть.
2. FROM — таблица (или таблицы), из которой извлекаются данные.
3. WHERE — условие фильтрации исходных строк.
4. GROUP BY — условие группировки.
5. HAVING — условие фильтрации сгруппированных данных.
6. ORDER BY — условие сортировки итогового результата.

Менять этот порядок нельзя.

Фильтрация данных: WHERE

Секция WHERE выполняется одной из первых, до склейки таблиц. Сначала отфильтровать данные, а потом производить с ними дальнейшие манипуляции — это экономичнее.

Условия в WHERE могут быть сложными и включать операторы AND и OR. Можно использовать следующие операторы сравнения:
= (равно), != или <> (не равно), >, <
LIKE — проверка строки по маске. Символ % означает любое количество любых символов.
BETWEEN ... AND ... — проверка на вхождение в интервал, включая границы.
IN (...) — проверка на вхождение в заданное множество значений. Элементами множества могут быть как константы, так и результат другого SELECT (подзапрос).

Можно использовать логический оператор NOT для отрицания условия (например, NOT BETWEEN).

Важная особенность WHERE в том, что фильтрация может проводиться по полям, которые вы не возвращаете в секции SELECT. SQL «видит» всю строку целиком.

Получение данных из нескольких таблиц

Данные можно извлекать из нескольких таблиц. Если в запросе FROM Students, Groups не указать условие склейки, то произойдет прямое произведение: каждая строка из таблицы Students будет сопоставлена с каждой строкой из таблицы Groups. Это редко соответствует желаемому результату.

Для корректного соединения в секции WHERE нужно указать условие склейки таблиц, обычно через равенство ключей. Например:
WHERE Students.GroupID = Groups.ID

Секция WHERE может одновременно содержать и условия склейки, и фильтры.

Можно соединять три и более таблиц:

sql
SELECT
Students.FName,
Students.LName,
Specialization.Name
FROM Students, Groups, Specialization
WHERE
Students.GroupID = Groups.ID
AND Groups.SpecID = Specialization.ID

Порядок перечисления таблиц в FROM не важен. Если поле с одинаковым именем существует в нескольких таблицах, к нему нужно обращаться через точку: ИмяТаблицы.ИмяПоля.

Агрегатные функции

Стандартный набор агрегатных функций в SQL:
MAX — максимальное значение.
MIN — минимальное значение.
AVG — среднее значение.
SUM — сумма значений.
COUNT — количество строк.

Если запрос возвращает только агрегатную функцию (например, SELECT MAX(Age) FROM Students), группировка (GROUP BY) не требуется.

Группировка данных: GROUP BY и фильтрация HAVING

Если наряду с агрегатной функцией возвращаются и другие, неагрегированные поля, необходимо использовать GROUP BY. В этой секции перечисляются те поля, по которым данные группируются для подсчета агрегата. GROUP BY всегда содержит поля, фигурирующие в SELECT вне агрегатных функций.

После группировки мы уже не имеем информации об отдельных строках — только об итоговых значениях. Поэтому для фильтрации по сгруппированным данным используется секция HAVING, а не WHERE.

Разница между WHERE и HAVING:
WHERE фильтрует исходные строки до группировки.
HAVING фильтрует результат после группировки.

Важный нюанс для HAVING:
• Можно писать условия с агрегатными функциями над любым полем (например, HAVING COUNT(Students.ID) > 10).
• Если в HAVING указывается фильтр по самому полю без агрегатной функции, то можно использовать только поля, присутствующие в GROUP BY.

Сортировка и устранение дубликатов

ORDER BY задает сортировку финального результата. По умолчанию она идет в естественном (прямом) порядке. Можно указать обратный порядок с помощью ключевого слова DESC (от англ. descending — убывающий). Сортировку можно делать по нескольким полям, для каждого из которых задается свое направление.

Опция DISTINCT указывается в SELECT и служит для исключения повторяющихся строк из результирующей выборки (например, SELECT DISTINCT LName FROM Students).

Вычисляемые поля, псевдонимы и оператор SELECT *

В SELECT можно не только извлекать поля таблиц, но и создавать вычисляемые поля, например: SELECT 2008 - Age AS BirthYear. Псевдоним столбцу задается с помощью ключевого слова AS (алиас). Исходное имя столбца в базе данных при этом не изменяется.

Оператор SELECT * возвращает все столбцы таблицы. Это удобно для быстрой проверки, но крайне неэкономично для промышленного использования по двум причинам:
1. СУБД сначала выполняет дополнительный запрос к системным таблицам, чтобы узнать список столбцов.
2. План исполнения для SELECT * не оптимизируется.

Поэтому для рабочих запросов следует всегда явно перечислять необходимые столбцы.

Краткие итоги

Теоретический фундамент языка SQL, основанный на принципах реляционной алгебры, гарантирует, что с помощью ограниченного набора операций можно решить любую задачу по извлечению данных. Это снимает ограничение на сложность запросов: если задача формализована, она разрешима средствами языка. Ключевым практическим следствием этого является переход от простого «доставания» данных к их конструированию.

Центральным элементом манипулирования данными выступает оператор SELECT. Его главная особенность, имеющая колоссальное прикладное значение, заключается в концептуальном разделении того, что мы видим в результате, и того, по каким критериям данные были отобраны. Возможность фильтровать строки по одному набору колонок, а возвращать совершенно другой, в том числе вычисляемый или взятый из иных таблиц, позволяет строить гибкие аналитические отчеты, не изменяя схему хранения.

Особую роль играет соединение таблиц, которое превращает базу данных из набора независимых справочников в единую информационную среду. Именно в секции WHERE одновременно решаются две задачи: восстановление связей между сущностями и содержательная фильтрация. Это смешение требует дисциплины, но дает возможность писать очень компактный и выразительный код, где из трех таблиц можно извлечь одну ячейку данных, попутно проверив условие из другой таблицы.

Переход от построчного анализа к групповому, реализуемый через GROUP BY и агрегатные функции, выводит работу с данными на принципиально иной уровень. Здесь возникает важный для практики водораздел, закрепленный синтаксически: фильтрация до группировки (WHERE) и после (HAVING). Понимание этого различия критически важно для производительности и корректности запросов. Эмпирическое правило состоит в том, чтобы как можно раньше, с помощью WHERE, максимально уменьшить объем обрабатываемых данных, оставляя HAVING только для условий, которые физически не могут быть проверены до агрегации.

Наконец, вопрос производительности напрямую затронут в дихотомии удобства и эффективности, олицетворяемой SELECT *. Конструкция, созданная для быстрого прототипирования, становится антипаттерном в промышленном коде, так как лишает оптимизатор СУБД возможности построить самый эффективный план выполнения. Это подводит к фундаментальному принципу разработки: платой за удобство написания часто становится снижение производительности исполнения, и ответственность за этот выбор лежит на разработчике.
DML: язык манипулирования данными

Основу практической работы с SQL составляет его DML-компонент (Data Manipulation Language). Ключевые операции DML — это SELECT (выборка), INSERT (вставка), DELETE (удаление) и UPDATE (обновление).

Структура оператора SELECT

SELECT — главный инструмент для получения данных. Его синтаксис имеет фиксированный порядок секций:
1. SELECT — перечень извлекаемых столбцов. Можно использовать * для выбора всех столбцов, но в рабочем коде это антипаттерн, так как СУБД не оптимизирует такой запрос и тратит время на поиск имен колонок в системных таблицах. Явное перечисление столбцов — всегда более производительный подход.
2. FROM — указывает таблицу-источник. Может содержать несколько таблиц через запятую.
3. WHERE — фильтрация строк. Критически важная особенность: WHERE выполняется на раннем этапе. Фильтровать данные до склейки таблиц или группировки — самое экономичное решение.
4. GROUP BY — группировка. Обязательна, если в SELECT есть одновременно и обычные, и агрегатные поля.
5. HAVING — фильтрация сгруппированных данных. Аналог WHERE, но работает после группировки.
6. ORDER BY — сортировка финального результата.

Фильтрация в секции WHERE

Секция WHERE способна «видеть» всю строку, поэтому фильтровать можно по полям, которые не возвращаются в SELECT.

Основные операторы для условий:
=, <>, >, < — стандартные операторы сравнения.
LIKE — сравнение строки с паттерном. Символ % заменяет любое количество символов.
BETWEEN ... AND ... — проверка на вхождение в диапазон, включая границы.
IN ( ... ) — проверка на принадлежность множеству. Внутри скобок может быть как список констант, так и подзапрос (вложенный SELECT).
AND, OR, NOT — логические операторы для комбинирования условий.

Соединение таблиц

Чтобы получить данные из нескольких таблиц, необходимо в секции WHERE прописать условие склейки, например:
WHERE Students.GroupID = Groups.ID

Если этого не сделать, произойдет прямое (декартово) произведение: каждая строка одной таблицы будет сопоставлена с каждой строкой другой, что почти всегда является ошибкой. Можно соединять три и более таблиц, последовательно указывая условия связи. Если имена столбцов в разных таблицах совпадают, к ним нужно обращаться уточненно: ИмяТаблицы.ИмяСтолбца.

Агрегатные функции и группировка

Для вычислений по набору строк используются агрегатные функции: COUNT (количество), SUM (сумма), AVG (среднее), MAX (максимум), MIN (минимум).

Если запрос возвращает только агрегаты, GROUP BY не нужен. Если вместе с агрегатом выводятся другие колонки, нужна группировка:
SELECT Department, AVG(Salary) FROM Employees GROUP BY Department;
В GROUP BY помещаются все неагрегированные поля из SELECT.

После группировки исходные строки уже недоступны. Поэтому для фильтрации по результатам агрегации используется HAVING. Например, чтобы оставить только отделы со средней зарплатой выше 50 000:
... GROUP BY Department HAVING AVG(Salary) > 50000

Ключевое правило: если в HAVING фильтр накладывается не на агрегатную функцию, а на само поле, то это поле обязательно должно быть в GROUP BY.

Дополнительные возможности

Алиасы (AS): позволяют задать псевдоним столбцу в выводе (SELECT Age * 2 AS DoubleAge).
Вычисляемые поля: можно выполнять арифметические операции прямо в SELECT.
DISTINCT: убирает дубликаты строк из результата (SELECT DISTINCT City FROM Clients).
Сортировка (ORDER BY): упорядочивает результат. DESC задает обратный порядок. Можно сортировать по нескольким полям, каждое из которых может иметь свое направление.

Выводы

1. DML — ключевая компонента SQL для манипуляции данными, включающая команды SELECT, INSERT, UPDATE, DELETE.
2. Синтаксис SELECT строг: порядок секций (SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY) не подлежит изменению.
3. Реляционная алгебра служит математическим фундаментом, гарантируя возможность решения любой задачи по выборке данных через ограниченный набор операций.
4. Секция WHERE фильтрует строки до группировки и позволяет использовать поля, которые не попадают в итоговую выборку.
5. Соединение нескольких таблиц требует явного указания условия склейки в WHERE для избежания прямого произведения.
6. Агрегатные функции (COUNT, SUM, AVG, MAX, MIN) вычисляют скалярное значение по набору строк.
7. Секция GROUP BY обязательна, если в SELECT наряду с агрегатными функциями присутствуют неагрегированные поля.
8. HAVING — это WHERE для агрегированных данных; он применяется после группировки и может фильтровать по агрегатным функциям.
9. ORDER BY управляет сортировкой финального результата, в том числе по нескольким полям с разными направлениями.
10. Использование SELECT * в промышленном коде недопустимо, так как эта операция лишена оптимизации и создает избыточную нагрузку.
11. Псевдонимы (AS) переименовывают столбцы только в выводе, а DISTINCT устраняет дубликаты строк результата.
12. Фильтры в WHERE можно гибко комбинировать, а операндом IN может выступать подзапрос, создавая вложенные конструкции любой глубины.

Вопросы для самопроверки

1. Чем DML-компонент SQL концептуально отличается от DDL-компонента с точки зрения решаемых задач?
2. Почему при соединении двух таблиц в секции FROM без указания условий в WHERE результат будет некорректным?
3. В каком порядке сервер баз данных логически обрабатывает секции в запросе SELECT?
4. Приведите пример ситуации, когда необходимо использовать HAVING, а не WHERE, и объясните почему.
5. В чем заключается разница в производительности между SELECT * и выборкой с явным перечислением всех столбцов?
6. Каким образом можно отфильтровать строки по полю, которое не должно отображаться в итоговом результате запроса?
7. Для чего используется алиас (alias) столбца и изменяет ли он структуру исходной таблицы?
8. Какие поля необходимо обязательно включить в секцию GROUP BY?
9. Как работает оператор IN и что можно использовать в качестве источника значений для проверки?
10. Каким образом можно задать сортировку сначала по убыванию одного поля, а затем по возрастанию другого?
11. Почему агрегатные функции нельзя использовать в секции WHERE?
12. Для чего нужна опция DISTINCT и в какой секции запроса она применяется?
Вернуться к учебному плану