Можно ли выполнить проекцию до соединения без изменения результата?
Да, но только если проекция сохраняет все атрибуты, необходимые для условия соединения, последующего фильтра, группировки и формирования результата. Если отбросить хотя бы один такой атрибут, соединение либо станет невозможным, либо его результат изменится.
Реляционная алгебра описывает запрос как последовательность операций над отношениями, включая проекцию и соединение. Такое формальное представление позволяет преобразовывать эквивалентные выражения и искать более эффективный порядок вычислений.
Перенос проекции ближе к источнику данных решает исходную проблему обработки лишних столбцов: промежуточные отношения становятся уже, уменьшаются затраты на память и передачу данных. В SQL подобные преобразования часто выполняет оптимизатор, даже если запрос написан в другом логическом порядке.
Пусть две таблицы соединяются по идентификатору, но до соединения из одной таблицы оставляют только столбцы, предназначенные для вывода. Если идентификатор соединения удалён, СУБД уже не может сопоставить строки по исходному условию.
Даже при сохранении ключа результат может измениться из-за семантики повторов. В классической реляционной алгебре отношения являются множествами, а SQL обычно сохраняет кратность строк, если явно не указано устранение дублей.
Проекцию можно протолкнуть перед соединением, если сохраняются:
Например, для соединения заказов с клиентами достаточно заранее оставить в заказах customer_id, если другие столбцы заказов нигде не используются:
Здесь ранняя проекция безопасна: столбец customer_id сохранён, а каждая строка заказа по-прежнему участвует в соединении. В SQL вложенный запрос без DISTINCT не устраняет повторы, поэтому несколько заказов одного клиента сохранят несколько строк результата.
Добавление DISTINCT уже не является нейтральным преобразованием: оно заменит множество заказов клиента одной строкой и изменит кратность результата. В реляционной алгебре устранение дублей является частью проекции над множествами, поэтому при переносе операции между реляционной алгеброй и SQL нужно учитывать различие между множествами и мультимножествами.
Практическое правило: сначала определяют зависимости последующих операций от столбцов, затем удаляют только действительно ненужные атрибуты. Это может ускорить запрос, но чрезмерная проекция способна привести к потере данных или потребовать повторного чтения исходной таблицы.
В отчёте нужно вывести имена клиентов, имеющих заказы. Один вариант соединяет полные строки заказов с клиентами, хотя из заказов нужен только customer_id. Рассматривались два решения: оставить полный набор столбцов, что проще, но увеличивает объём промежуточных данных, или выполнить раннюю проекцию, что уменьшает ширину строк, но требует проверить все последующие зависимости.
Выбрано раннее сохранение только customer_id без DISTINCT. Это сохраняет кратность заказов и не меняет результат исходного соединения, одновременно уменьшая объём обрабатываемых данных. Если задача формулировалась бы как проверка самого факта наличия заказа, вместо этого можно было бы использовать EXISTS, поскольку тогда размножение строк не требовалось бы вообще.
1. Какие именно столбцы нельзя удалять перед соединением?
Нельзя удалять столбцы, участвующие в условии соединения, независимо от того, выводятся они пользователю или нет. Также нужно сохранить столбцы для последующих условий фильтрации, агрегирования, сортировки и вычисления выражений. Сохранение только столбцов из списка вывода недостаточно.
2. Почему устранение дублей при ранней проекции может изменить результат?
В SQL две строки с одинаковым набором выбранных значений обычно остаются двумя строками, если не применено DISTINCT. Если добавить DISTINCT до соединения, несколько исходных строк могут превратиться в одну, и последующее соединение даст меньшую кратность результата. Это особенно заметно при связях «один-ко-многим» и при последующем COUNT.
3. Всегда ли ранняя проекция ускоряет запрос?
Нет. Уменьшение ширины промежуточных строк обычно полезно, но само преобразование может не дать эффекта, если оптимизатор и так выполняет его автоматически. Кроме того, раннее устранение дублей требует сортировки или хеширования, а потеря столбца, нужного для индекса, фильтра или соединения, может ухудшить план. Поэтому эквивалентность преобразования проверяют логически, а выгоду — по фактическому плану выполнения и измерениям.