Порядок выполнения SQL-запроса
Я запутался с порядком выполнения этого запроса, пожалуйста, объясните мне это. Меня смущает, когда применяется соединение, вызывается функция, добавляется новый столбец с регистром и когда добавляется серийный номер. Пожалуйста, объясните порядок исполнения всего этого.
select Row_number() OVER(ORDER BY (SELECT 1)) AS 'Serial Number',
EP.FirstName,Ep.LastName,[dbo].[GetBookingRoleName](ES.UserId,EP.BookingRole) as RoleName,
(select top 1 convert(varchar(10),eventDate,103)from [3rdi_EventDates] where EventId=13) as EventDate,
(CASE [dbo].[GetBookingRoleName](ES.UserId,EP.BookingRole)
WHEN '90 Day Client' THEN 'DC'
WHEN 'Association Client' THEN 'DC'
WHEN 'Autism Whisperer' THEN 'DC'
WHEN 'CampII' THEN 'AD'
WHEN 'Captain' THEN 'AD'
WHEN 'Chiropractic Assistant' THEN 'AD'
WHEN 'Coaches' THEN 'AD'
END) as Category from [3rdi_EventParticipants] as EP
inner join [3rdi_EventSignup] as ES on EP.SignUpId = ES.SignUpId
where EP.EventId = 13
and userid in (
select distinct userid from userroles
--where roleid not in(6,7,61,64) and roleid not in(1,2))
where roleid not in(19, 20, 21, 22) and roleid not in(1,2))
Это функция, которая вызывается из вышеуказанного запроса.
CREATE function [dbo].[GetBookingRoleName]
(
@UserId as integer,
@BookingId as integer
)
RETURNS varchar(20)
as
begin
declare @RoleName varchar(20)
if @BookingId = -1
Select Top 1 @RoleName=R.RoleName From UserRoles UR inner join Roles R on UR.RoleId=R.RoleId Where UR.UserId=@UserId and R.RoleId not in(1,2)
else
Select @RoleName= RoleName From Roles where RoleId = @BookingId
return @RoleName
end
7 ответов
У SQL нет порядка выполнения. Это декларативный язык. Оптимизатор может выбрать любой заказ, который он считает подходящим для обеспечения наилучшего времени исполнения. При любом запросе SQL никто не может притвориться, что знает порядок выполнения. Если вы добавите подробную информацию о задействованной схеме (точное определение таблиц и индексов) и предполагаемом количестве элементов (размер данных и селективность ключей), можно сделать предположение о вероятном порядке выполнения.
В конечном счете, единственный правильный "порядок" - это описанный фактический план выполнения. См. Раздел " Просмотр планов выполнения с использованием классов событий SQL Server Profiler" и " Просмотр графических планов выполнения" (SQL Server Management Studio).
Совершенно иная вещь - как запросы, подзапросы и выражения проецируют себя в 'validity'. Например, если у вас есть псевдоним в списке проекции SELECT, можете ли вы использовать псевдоним в предложении WHERE? Как это:
SELECT a+b as c
FROM t
WHERE c=...;
Является ли использование c
псевдоним действителен в предложении where? Ответ - нет. Запросы формируют синтаксическое дерево, и нижняя ветвь дерева не может быть ссылкой на что-то определенное выше в дереве. Это не обязательно порядок "исполнения", это скорее проблема синтаксического разбора. Это эквивалентно написанию этого кода на C#:
void Select (int a, int b)
{
if (c = ...) then {...}
int c = a+b;
}
Как и в C#, этот код не будет компилироваться, потому что переменная c
используется до того, как определено, SELECT выше не будет компилироваться должным образом, потому что псевдоним c
ссылка в дереве ниже, чем определено на самом деле.
К сожалению, в отличие от хорошо известных правил синтаксического анализа языка C / C#, правила SQL о том, как строится дерево запросов, являются каким-то эзотерическими. В разделе "Обработка операторов единого SQL" есть краткое упоминание о них, но подробно рассказывается о том, как они создаются и какой порядок действителен, а какой нет, я не знаю ни одного источника. Я не говорю, что нет хороших источников, я уверен, что некоторые из хороших книг по SQL освещают эту тему.
Обратите внимание, что порядок дерева синтаксиса не соответствует визуальному порядку текста SQL. Например, предложение ORDER BY обычно является последним в тексте SQL, но как дерево синтаксиса оно располагается выше всего остального (оно сортирует выходные данные SELECT, поэтому оно располагается, так сказать, над столбцами SELECTed), и поэтому является действителен для ссылки на c
псевдоним:
SELECT a+b as c
FROM t
ORDER BY c;
обновленный
На самом деле есть это: Порядок логической обработки оператора SELECT
Следующие шаги показывают логический порядок обработки или порядок привязки для инструкции SELECT. Этот порядок определяет, когда объекты, определенные на одном шаге, становятся доступными для предложений на последующих шагах. Например, если обработчик запросов может связывать (обращаться) к таблицам или представлениям, определенным в предложении FROM, эти объекты и их столбцы становятся доступными для всех последующих шагов. И наоборот, поскольку предложение SELECT является шагом 8, любые псевдонимы столбцов или производные столбцы, определенные в этом предложении, не могут быть указаны в предыдущих предложениях. Однако на них могут ссылаться последующие пункты, такие как предложение ORDER BY. Обратите внимание, что фактическое физическое выполнение оператора определяется обработчиком запросов, и порядок может отличаться от этого списка.
- ОТ
- НА
- ПРИСОЕДИНИТЬСЯ
- ГДЕ
- ГРУППА ПО
- С КУБОМ или С РОЛЛУПОМ
- HAVING
- ВЫБРАТЬ
- DISTINCT
- СОРТИРОВАТЬ ПО
- ТОП
Запросы обычно обрабатываются в следующем порядке (SQL Server). Я понятия не имею, если другие СУБД делают это таким образом.
FROM [MyTable]
ON [MyCondition]
JOIN [MyJoinedTable]
WHERE [...]
GROUP BY [...]
HAVING [...]
SELECT [...]
ORDER BY [...]
SQL-запрос не является императивным, а декларативным, поэтому вы не знаете, какой оператор выполняется первым, но поскольку SQL оценивается механизмами запросов SQL, большинство механизмов SQL следует аналогичному процессу для получения результатов. Возможно, вам придется понять, как механизм запросов работает внутри, чтобы понять поведение выполнения SQL.
У Джулии Эвенс есть отличный пост, объясняющий это, стоит его проверить:
https://jvns.ca/blog/2019/10/03/sql-queries-don-t-start-with-select/
SQL - это декларативный язык, то есть он говорит движку SQL, что делать, а не как. Это контрастирует с императивным языком, таким как C, в котором четко изложено, как сделать что-то.
Это означает, что не все операторы будут выполняться так, как ожидалось. Особого внимания заслуживают логические выражения, которые могут не оцениваться слева направо, как написано. Например, следующий код не гарантированно выполняется без ошибки деления на ноль:
SELECT 'null' WHERE 1 = 1 OR 1 / 0 = 0
Причина этого в том, что оптимизатор запросов выбирает лучший (наиболее эффективный) способ выполнения оператора. Это означает, что, например, значение может быть загружено и отфильтровано до применения предиката преобразования, что вызывает ошибку. Смотрите вторую ссылку выше для примера
"Порядок выполнения", вероятно, является плохой мысленной моделью для SQL-запросов. Трудно написать один запрос, который будет зависеть от порядка выполнения (это хорошо). Вместо этого вы должны думать обо всех соединениях и о том, где предложения происходят одновременно (почти как шаблон)
Тем не менее, вы можете запустить отображение планов выполнения, которые должны дать вам представление о нем.
Однако, поскольку неясно, почему вы хотите знать порядок выполнения, я предполагаю, что вы пытаетесь получить мысленную модель для этого запроса, чтобы вы могли каким-то образом исправить это. Вот как я бы "перевел" ваш запрос, хотя я хорошо справился с таким анализом, но есть некоторая серая область с его точностью.
ОТ И ГДЕ ОГОВОРКА
Дайте мне все строки участников мероприятия.
from [3rdi_EventParticipants
Также дайте мне все строки регистрации событий, которые соответствуют строкам участников событий в SignUpID
inner join 3rdi_EventSignup] as ES on EP.SignUpId = ES.SignUpId
Но только для события 13
EP.EventId = 13
И только если идентификатор пользователя имеет запись в таблице ролей пользователя, где идентификатор роли не находится в 1,2,19,20,21,22
userid in (
select distinct userid from userroles
--where roleid not in(6,7,61,64) and roleid not in(1,2))
where roleid not in(19, 20, 21, 22) and roleid not in(1,2))
Выберите пункт
Для каждой из строк дайте мне уникальный идентификатор
Row_number() OVER(ORDER BY (SELECT 1)) AS 'Serial Number',
Имя участника
EP.FirstName
Фамилия участников
Ep.LastName
Название роли бронирования
GetBookingRoleName
Зайдите в "Даты событий" и выясните, какая первая EventDate, где EventId = 13, вы найдете
(select top 1 convert(varchar(10),eventDate,103)from [3rdi_EventDates] where EventId=13) as EventDate
Наконец, переведите GetBookingRoleName в категорию. У меня нет таблицы для этого, поэтому я сопоставлю ее вручную
(CASE [dbo].[GetBookingRoleName](ES.UserId,EP.BookingRole)
WHEN '90 Day Client' THEN 'DC' WHEN 'Association Client' THEN 'DC' WHEN 'Autism Whisperer' THEN 'DC' WHEN 'CampII' THEN 'AD' WHEN 'Captain' THEN 'AD' WHEN 'Chiropractic Assistant' THEN 'AD' WHEN 'Coaches' THEN 'AD' END) as Category
Итак, пара заметок здесь. Вы ничего не заказываете, когда выбираете TOP. Вы, вероятно, должны иметь заказ там. Вы также можете легко добавить это в пункт from, например:
from [3rdi_EventParticipants] as EP
inner join [3rdi_EventSignup] as ES on EP.SignUpId = ES.SignUpId,
(select top 1 convert(varchar(10),eventDate,103)
from [3rdi_EventDates] where EventId=13
Order by eventDate) dates
Существует логический порядок оценки текста запроса, но ядро базы данных может выбирать, в каком порядке выполнять компоненты запроса, основываясь на том, что является наиболее оптимальным. Логический порядок разбора текста приведен ниже. Вот почему, например, вы не можете использовать псевдоним из SELECT
пункт в WHERE
пункт. Что касается процесса анализа запроса, псевдоним еще не существует.
ОТ
НА
ВНЕШНИЙ
ГДЕ
ГРУППА ПО
КУБ | СВЕРНУТЬ
HAVING
ВЫБРАТЬ
DISTINCT
СОРТИРОВАТЬ ПО
ТОП
См. Документацию Microsoft (см. "Порядок логической обработки оператора SELECT") для получения дополнительной информации об этом.
Упрощенный порядок для оператора T-SQL -> SELECT:
1) FROM
2) Cartesian product
3) ON
4) Outer rows
5) WHERE
6) GROUP BY
7) HAVING
8) SELECT
9) Evaluation phase in SELECT
10) DISTINCT
11) ORDER BY
12) TOP
как я делал до сих пор - тот же порядок был применим в SQLite.
Источник => ВЫБРАТЬ (Transact-SQL)
... конечно, есть (редкие) исключения.