Декларативность, или как выполняется ваш SQL-запрос
Что такое декларативность SQL и как СУБД выполняет ваш запрос
Теория
Сила SQL в том, что он снимает с человека лишнюю нагрузку. Вам не нужно расписывать пошаговый алгоритм выполнения вашего запроса. Вы только формулируете конечную цель. СУБД берёт на себя маршрут.
В этом и состоит декларативность. Запрос сообщает, какой результат вы хотите получить на выходе, а не как его нужно получить. Как если бы вы пришли в ресторан и заказали на ужин блюдо, не вдаваясь в подробности как оно будет приготовлено. Процедуру приготовления блюда берет на себя кухня. Так вот кухня в этом примере и есть СУБД. Вы пишете SQL запрос, СУБД выбирает алгоритм выполнения вашего запроса.
Дело в том, что для получения одного и того же результата часто можно написать запрос разными способами. Более того, одинаково написанный запрос на разных данных может выполняться разными алгоритмами внутри СУБД. Хорошая бытовая аналогия здесь — навигатор. Вы не диктуете ему каждый поворот. Вы задаёте точку назначения. Дальше система сама прокладывает путь. Иногда один маршрут окажется короче, иногда другой. Цель поездки будет одна, но траектория и расстояние до цели могут отличаться. С SQL история очень похожая.
Из-за этого один и тот же запрос может выполняться по-разному в разных условиях. Меняется объём таблиц, появляются индексы, обновляется статистика — и PostgreSQL имеет право выбрать другой путь выполнения. Смысл запроса при этом остаётся прежним.
Чтобы это не звучало сложно, попробуйте представить себе путь запроса от момента его написания и запуска до момента появления результата на вашем экране. SQL-запрос сначала воспринимается СУБД как простой текст. Посмотрим, что же происходит дальше. Запрос проходит несколько стадий: текст запроса разбирается, структура проверяется, объекты базы данных сверяются, строится внутреннее представление, при необходимости происходит переписывание, потом выбирается план (путь, о котором мы говорили выше), и только после этого начинается фактическая работа с данными, то есть выполнение.
Лексическая проверка начинается с разбора самого текста запроса. PostgreSQL читает запрос и делит его на отдельные элементы: ключевые слова, имена таблиц, имена столбцов, строки в кавычках, числа, операторы и другие части конструкции. Такие элементы обычно называют токенами. После этого система видит не сплошной текст, а набор понятных ей элементов.
Синтаксическая проверка смотрит, можно ли из этих элементов собрать корректный SQL-запрос. Идёт проверка грамматики: на своём ли месте стоит SELECT, после него ли указан список столбцов, правильно ли записаны FROM, WHERE, ORDER BY и другие части запроса.
Семантическая проверка отвечает за соответствие запроса базе данных. Существует ли в базе данных таблица, к которой мы обращаемся в запросе? Существует ли столбец, который мы указали в запросе? Можно ли применить эту функцию к такому типу данных? На этом этапе PostgreSQL отсекает запросы, которые выглядят как нормальный SQL, но по факту обращаются к несуществующим таблицам или столбцам либо пытаются выполнить недопустимую операцию.
Дерево разбора — это внутренняя схема запроса. Из первоначального текста запроса PostgreSQL строит для своего удобства специальное дерево, то есть некую структуру, в которой отдельно видны источник данных, список столбцов, условие фильтрации, сортировка и другие части конструкции.
Переписывание выполняется не всегда, но как этап целого алгоритма существует. Внутренняя форма запроса может быть приведена к более удобному виду перед следующим шагом. Ответ запроса от этого не меняется. Меняется только то, как он представлен внутри системы.
Планирование — это выбор пути выполнения вашего SQL запроса, самый сложный и важный этап, от которого зависит эффективность вашего запроса с точки зрения производительности. Один и тот же SQL-запрос часто можно выполнить разными способами. Например, можно читать таблицу целиком, а можно выборочно через индекс (специальный объект в БД значительно ускоряющий выполнение). Можно сначала отфильтровать строки, а потом их отсортировать, а можно выстроить другой порядок операций. PostgreSQL сравнивает такие варианты и пытается выбрать лучший. Вопрос здесь в том, а что значит лучший?
У каждого пути выполнения запроса, то есть плана есть своя стоимость плана. Здесь стоимость - это не деньги, а внутренняя оценка того, насколько тяжёлым будет маршрут выполнения вашего запроса. Стоимость складывается из предполагаемых операций внутри запроса: сколько строк придётся прочитать, нужна ли сортировка, будет ли соединение таблиц, есть ли смысл использовать индекс, сколько данных придётся передать между операциями. Затем PostgreSQL сравнивает получившиеся оценки и выбирает тот план, у которого ожидаемая стоимость ниже остальных.
Выполнение — это фактическая работа по тому плану, который СУБД выбрала на предыдущем этапе планирования. СУБД читает строки, проверяет фильтр, сортирует, агрегирует, соединяет данные, если нужно, и возвращает готовый результат вам на экран.
Примеры
1. Один запрос и весь его внутренний маршрут (нажмите кнопку запустить, чтобы увидеть результат)
Ниже мы увидим все этапы, через которые он проходит внутри PostgreSQL. Переключайте этапы последовательно, чтобы увидеть весь путь запроса от момента написания, до момента выполнения.
Типичные ошибки
Путают текст запроса и план выполнения
Запрос пишет человек. План строит PostgreSQL. Это не одно и то же. Один и тот же SQL запрос может получить разные планы в разных условиях.
Смешивают синтаксическую и семантическую ошибку
Синтаксис отвечает за форму конструкции. Семантика отвечает за смысл относительно объектов базы. У запроса может быть правильная форма, но неверная ссылка на таблицу или столбец.
Думают, что SQL выполняется сверху вниз как обычный скрипт
Система сначала разбирает запрос, затем строит внутреннюю модель, после этого выбирает план и лишь потом работает с данными. Между написанным текстом запроса и реальным выполнением лежит целая цепочка внутренних операций.
Ожидают, что план всегда будет одинаковым
PostgreSQL выбирает путь на основе оценки стоимости. Если меняются размеры таблиц, статистика или индексы, план тоже может поменяться.
Недооценивают переписывание запроса
Этот этап не всегда заметен глазами, но он существует. Внутренняя форма запроса перед планированием может отличаться от того текста, который написал пользователь.
Практика
Проверь себя
Ответьте на вопросы, чтобы закрепить материал: