Мы переходим к компоненте языка 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 *. Конструкция, созданная для быстрого прототипирования, становится антипаттерном в промышленном коде, так как лишает оптимизатор СУБД возможности построить самый эффективный план выполнения. Это подводит к фундаментальному принципу разработки: платой за удобство написания часто становится снижение производительности исполнения, и ответственность за этот выбор лежит на разработчике.
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 и в какой секции запроса она применяется?