Почему запросы анализируются таким образом, что запрещает использование псевдонимов столбцов в большинстве разделов?

16

Пытаясь написать запрос, я обнаружил (сложным способом), что SQL Server анализирует WHERE в запросе задолго до синтаксического анализа SELECT при выполнении запроса.

Документы MSDN говорят, что общий порядок логического синтаксического анализа таков, что SELECT анализируется почти последним (что приводит к ошибкам «без такого объекта [псевдоним]» при попытке использовать псевдоним столбца в других разделах). Было даже предложение разрешить использование псевдонимов в любом месте, которое было сбито командой Microsoft, ссылаясь на проблемы соблюдения стандартов ANSI (что говорит о том, что это поведение является частью стандарта ANSI).

Как программист (не администратор БД), я обнаружил, что это поведение несколько сбивает с толку, так как мне кажется, что оно в значительной степени противоречит цели иметь псевдонимы столбцов (или, по крайней мере, псевдонимы столбцов можно было бы сделать значительно более мощными, если бы они были анализируемый ранее при выполнении запроса), поскольку единственное место, где вы можете фактически использовать псевдонимы, находится в ORDER BY. Как программист, кажется, что он упускает огромную возможность сделать запросы более мощными, удобными и сухими.

Похоже, что это настолько вопиющая проблема, что, разумеется, есть основания полагать, что есть и другие причины для того, чтобы решить, что псевдонимы столбцов не должны допускаться ни в чем, кроме SELECT и ORDER BY, но каковы эти причины?

Shauna
источник

Ответы:

19

Резюме

Там нет никакой логической причины , это не может быть сделано, но польза невелика , и есть некоторые подводные камни , которые могут быть не сразу.

Результаты исследований

Я провел небольшое исследование и нашел хорошую информацию. Ниже приводится прямая цитата из надежного первоисточника (который хочет остаться анонимным) в 2012-08-09 17:49 GMT:

Когда SQL был впервые изобретен, у него не было псевдонимов в предложении SELECT. Это был серьезный недостаток, который был исправлен, когда язык был стандартизирован ANSI примерно в 1986 году.

Язык был задуман как «непроцедурный» - другими словами, для описания данных, которые вы хотите, без указания того, как их найти. Итак, насколько я знаю, нет никаких причин, по которым реализация SQL не могла бы проанализировать весь запрос перед его обработкой и позволить определять псевдонимы где угодно и использовать везде. Например, я не вижу причин, по которым следующий запрос не должен быть действительным:

select name, salary + bonus as pay
from employee
where pay > 100000

Хотя я думаю, что это разумный запрос, некоторые системы на основе SQL могут вводить ограничения на использование псевдонимов по некоторым причинам, связанным с реализацией. Я не удивлен, узнав, что SQL Server делает это.

Я заинтересован в дальнейших исследованиях стандарта SQL-86 и в том, почему современные СУБД не поддерживают повторное использование псевдонимов, но у них еще не было времени продвинуться далеко вперед. Для начала, я не знаю, где взять документацию или как узнать, кто именно составлял комитет. Кто-нибудь может помочь? Я также хотел бы узнать больше об оригинальном продукте Sybase, из которого вышел SQL Server.

Из этого исследования и некоторых дальнейших размышлений я пришел к выводу, что использование псевдонимов в других разделах, хотя и вполне возможно, просто никогда не было таким высоким приоритетом для производителей СУБД по сравнению с другими языковыми функциями. Поскольку это не так уж и много препятствий, так как разработчик запросов легко его обходит, то прилагать усилия по сравнению с другими усовершенствованиями не оптимально. Кроме того, он будет проприетарным, поскольку он явно не является частью стандарта SQL (хотя я жду, чтобы узнать об этом наверняка), и, таким образом, будет незначительным улучшением, нарушающим совместимость SQL между СУБД. Для сравнения CROSS APPLY(что на самом деле является не чем иным, как производной таблицей, допускающей внешние ссылки), это огромное изменение, которое в то время как проприетарная предлагает невероятную выразительную силу, которую нелегко выполнить другими способами.

Проблемы с использованием псевдонимов везде

Если вы разрешите помещать элементы SELECT в предложение WHERE, вы можете не только разбить сложность запроса (и, следовательно, сложность нахождения хорошего плана выполнения), но это может привести к совершенно нелогичным вещам. Пытаться:

SELECT X + 5 Y FROM MyTable WHERE Y = X

Что если MyTable уже имеет столбец Y, на который ссылается предложение WHERE? Решение состоит в том, чтобы использовать CTE или производную таблицу, которая в большинстве случаев не требует дополнительных затрат, но достигает того же конечного конечного результата. CTE и производные таблицы, по крайней мере, обеспечивают разрешение неоднозначности, позволяя использовать псевдоним только один раз.

Кроме того, исключительное использование псевдонимов в предложении FROM. Вы не можете сделать это:

SELECT
   T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
FROM
   Table1 T
   INNER JOIN Table2 T2
      ON T2.ID = CalcID
   INNER JOIN Table3 T3
      ON T2.ID = T3.ID

Это циклическая ссылка (в том смысле, что T2 тайно ссылается на значение из T3, до того, как эта таблица была представлена ​​в списке JOIN), и чертовски трудно увидеть. Как насчет этого:

INSERT dbo.FinalTransaction
SELECT
   newid() FinalTransactionGUID,
   'GUID is: ' + Convert(varchar(50), FinalTransactionGUID) TextGUID,
   T.*
FROM
   dbo.MyTable T

Сколько вы хотите поспорить, что функция newid () будет дважды включена в план выполнения, совершенно неожиданно заставив два столбца показывать разные значения? Как насчет того, когда вышеупомянутый запрос используется N уровней глубоко в CTE или производных таблицах. Я гарантирую, что проблема хуже, чем вы можете себе представить. Есть уже серьезные проблемы несогласованности о том, когда вещи оцениваются только один раз или в какой момент в плане запроса, и Microsoft заявила , что она не исправитнекоторые из них, потому что они правильно выражают алгебру запросов - если получаются неожиданные результаты, разбейте запрос на части. Разрешение цепных ссылок, обнаружение циклических ссылок через потенциально очень длинные такие цепочки - это довольно сложные проблемы. Внедрите параллелизм, и у вас будет кошмар в процессе становления.

Примечание. Использование псевдонима в WHERE или GROUP BY не изменит проблемы с такими функциями, как newid () или rand ().

SQL Server способ создания выражений многократного использования

CROSS APPLY / OUTER APPLY - это один из способов SQL Server для создания выражений, которые можно использовать в любом другом месте запроса (только не ранее в предложении FROM):

SELECT
   X.CalcID
FROM
   Table1 T
   INNER JOIN Table3 T3
      ON T.ID = T3.ID
   CROSS APPLY (
      SELECT
         T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
   ) X
   INNER JOIN Table2 T2
      ON T2.ID = X.CalcID

Это делает две вещи:

  1. Делает все выражения в CROSS APPLY получать «пространство имен» (псевдоним таблицы, здесь, X) и быть уникальным в этом пространстве имен.
  2. Повсюду становится очевидным не только то, что CalcID приходит из X, но также становится очевидным, почему вы не можете использовать что-либо из X при объединении таблиц T1 и T3, потому что X еще не был представлен.

Я на самом деле очень люблю CROSS APPLY. Он стал моим верным другом, и я использую его все время. Вам нужен частичный UNPIVOT (для которого потребуется PIVOT / UNPIVOT или UNPIVOT / PIVOT с использованием собственного синтаксиса)? Сделано с CROSS APPLY. Нужна расчетная величина, которая будет многократно использоваться? Выполнено. Нужно жестко навязать порядок выполнения вызовов через связанный сервер? Готово - с кричащим улучшением скорости. Нужно только один тип строки разделить на 2 строки или с дополнительными условиями? Выполнено.

Поэтому, по крайней мере, в СУБД SQL Server 2005 и более поздних версиях у вас больше нет причин для жалоб: CROSS APPLY - это то, как вы СУШИТЕ так, как вам хочется.

ErikE
источник
14

Я не могу сказать вам точные причины, но я скажу вам, что есть обходные пути для повторяющихся выражений, например, использование CTE, подзапросов, производных таблиц и т. Д., Чтобы избежать повторения.

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

Это все сказал, я также повторю часть моего ответа от этого вопроса:

/dba/19762/why-is-the-select-clause-listed-first


Вот объяснение того, как обрабатывается запрос в соответствии со стандартом, Джо Селко (я украл это из моей собственной статьи на aspfaq.com , в которой, вероятно, украл цитату из сообщения группы новостей Celko):

Вот как SELECT работает в SQL ... по крайней мере, в теории. Реальные продукты будут оптимизировать вещи, когда они могут.

Начните с предложения FROM и создайте рабочую таблицу из всех соединений, объединений, пересечений и любых других конструкторов таблиц. Опция AS позволяет вам дать имя этой рабочей таблице, которое затем вы должны будете использовать для остальной части содержащего запрос.

Перейдите к предложению WHERE и удалите строки, которые не соответствуют критериям; то есть, что не проверить на ИСТИНА (отклонить НЕИЗВЕСТНО и ЛОЖЬ). Предложение WHERE применяется к работе в предложении FROM.

Перейдите к необязательному предложению GROUP BY, создайте группы и сведите каждую группу к одной строке, заменив исходную рабочую таблицу новой сгруппированной таблицей. Строки сгруппированной таблицы должны быть характеристиками группы: (1) столбец группировки (2) статистика о группе (т.е. агрегатные функции) (3) функция или (4) выражение, составленное из этих трех элементов.

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

Перейдите к предложению SELECT и создайте выражения в списке. Это означает, что скалярные подзапросы, вызовы функций и выражения в SELECT выполняются после выполнения всех других предложений. Оператор AS также может дать имя выражениям в списке SELECT. Эти новые имена появляются сразу, но после выполнения предложения WHERE; по этой причине вы не можете использовать их в списке SELECT или WHERE.

Вложенные выражения запроса следуют обычным правилам области видимости, которые можно ожидать от блочного структурированного языка, такого как C, Pascal, Algol и т. Д. А именно, самые внутренние запросы могут ссылаться на столбцы и таблицы в запросах, в которых они содержатся.

Это означает, что SELECT не может иметь больше столбцов, чем GROUP BY; но это, конечно, может иметь меньше столбцов.

Теперь Celko был одним из основных участников более ранних версий стандартов. Я не знаю, получите ли вы когда-нибудь окончательный ответ на этот WHY?вопрос, кроме предположений. Я предполагаю, что перечисление фактической операции в первую очередь позволяет парсеру очень точно знать, какой будет тип операции. Представьте себе объединение из 20 таблиц, которое может в конечном итоге стать SELECTили или UPDATEили DELETE, и помните, что код для этих механизмов был изначально написан в те времена, когда разбор строк был довольно дорогостоящим.

Обратите внимание, что если стандарт SQL продиктован FROMв первую очередь, поставщики, возможно, самостоятельно решили проанализировать грамматику в другом порядке, поэтому все еще может не иметь смысла ожидать, что порядок предложений, как написано, полностью подчиняется порядку обработки 100% время.

То же самое верно и для таких вещей, как CASE. Мы видели сценарии прямо здесь, на этом сайте , например, где ранее веровавший миф, который CASEвсегда обрабатывает в порядке и коротких замыканиях, является ложным. И это распространяется и на другие распространенные убеждения, такие как оценка SQL Server объединений в порядке их написания, короткие замыкания WHEREслева направо или обработка CTE один или в определенном порядке, даже если на них ссылаются несколько раз. Продукты могут свободно оптимизировать то, как они считают нужным, даже если это не совсем точно отражает то, как вы заявили, что запрос должен работать декларативно.

Аарон Бертран
источник
2
Также обратите внимание, что возможность использовать или не использовать псевдонимы в разных частях запроса обеспечивается анализатором, а не оптимизатором или механизмом выполнения. То, как механизм фактически выполняет запрос, не обязательно отражает ограничения, влияющие на синтаксис.
Аарон Бертран
2

В Entity SQL вы МОЖЕТЕ использовать псевдонимы из выражений в других местах запроса в некоторых ситуациях:

select k1, count(t.a), sum(t.a)
from T as t
group by t.b + t.c as k1

Обратите внимание, что здесь вы ДОЛЖНЫ определить выражение в GROUP BYпредложении, чтобы использовать его в SELECTпредложении.

Это, очевидно , можно разрешить некоторые из такого рода псевдоним как-многоразовой-выражения в запросах SQL.

ErikE
источник