Логический порядок обработки инструкции select

Понимание логической обработки запросов — ключ к разработке производительного кода SQL

Предлагаю вашему вниманию первую статью из серии материалов, посвященных логической обработке запросов. В ней речь пойдет о том, что я понимаю под этим термином, и будет предложен общий обзор проблемы. В следующих статьях я подробно расскажу об отдельных предложениях запроса.

Если бы меня попросили назвать фундаментальный вопрос в T-SQL, наиболее важный для практической работы, то, несомненно, я бы выбрал логическую обработку запросов. Необходимо с самого начала отметить, что этот термин не взят из какого-то формального источника. Я придумал его, чтобы объяснить студентам и читателям логическую, или концептуальную, интерпретацию запроса. Глубокое понимание этой темы — ключ к созданию правильного и надежного программного кода. Всегда на занятиях по T-SQL, будь то курсы для начинающих или для опытных специалистов, я начинаю с этой темы, поскольку она одинаково важна для всех.

Что означает «логическая обработка запросов»?

Чтобы понять, что здесь означает слово «логическая», нужно начать с основ T-SQL. Это широко известные базовые принципы, их взаимосвязь показана на рисунке 1.

Основы T-SQL
Рисунок 1. Основы T-SQL

T-SQL, или Transact-SQL, — диалект стандартного языка SQL, используемый Microsoft в нескольких продуктах, которые являются частью платформы обработки данных компании, таких как Microsoft SQL Server и Microsoft Azure SQL Database. Используя T-SQL, можно управлять данными в этих продуктах. T-SQL — собственный диалект Microsoft — базируется в основном на стандартном языке SQL с некоторыми расширениями. Язык SQL (Structured Query Language) поддерживается как Международной организацией по стандартизации (ISO), так и Американским национальным институтом стандартов (ANSI).

SQL, в свою очередь, основывается на реляционной модели — семантической модели представления данных, созданной Эдгаром Ф. Коддом в 1969 году. Реляционная же модель основана на двух областях математики: теории множеств и логике предикатов.

В реляционной модели определен важный принцип независимости физических данных. Это означает, что модель и основанный на ней язык определяют логические аспекты данных и манипуляций или, другими словами, смысл. Авторы модели избегают вдаваться в детали физической реализации (способы физической организации, хранения и доступа к данным, а также методы физической обработки — оптимизации и выполнения — запросов). Физическая часть зависит от используемой платформы базы данных. Не предполагается, что пользователь осмысливает информацию, основываясь на физических данных. Для этого служит логическая модель.

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

Реализация Microsoft построена с учетом принципа независимости физических данных и потому не гарантирует, что данные после запроса будут получены в некотором особом порядке, если только во внешний запрос не добавлено предложение ORDER BY. Похожее нарушение принципа происходит, когда пользователи обновляют данные, и правильность решения зависит от обновления данных в порядке кластеризованного индекса (поищите в Интернете quirky update и узнаете, что имеется в виду).

Поэтому под логической обработкой запросов подразумевается логическая, или концептуальная, интерпретация запроса. Она основывается на стандарте SQL, в котором описано, как данные из входных таблиц преобразуются в ходе последовательности шагов в окончательный результат запроса. Это описание не зависит от каких-либо факторов физической интерпретации. Таким образом, изучая данную тему, важно не придавать значения показателям производительности — они не относятся к зоне ответственности SQL или реляционной модели.

Порядок логической обработки запросов

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

Порядок ввода предложений запросов с номерами шагов логической обработки запросов
Рисунок 2. Порядок ввода предложений запросов с номерами шагов логической обработки запросов

Предложения основного запроса показаны в том порядке, в котором их нужно вводить, а номера шагов представляют порядок логической обработки запросов. Обратите внимание, что, хотя вы вводите предложение SELECT первым, предложение FROM логически обрабатывается в первую очередь. На самом деле предложение SELECT логически обрабатывается только на пятом шаге.

Я поясню, что стоит за такой структурой. Дело в том, что разработчики SQL хотели, чтобы язык напоминал английский. Первоначальное название языка было SEQUEL (сокращение от Structured English QUEry Language), но из-за спора об авторских правах оно было изменено на SQL. Теперь вспомним, как структурированы инструкции в английском языке. Например, рассмотрим запрос Bring me the T-SQL Querying book from the shelf in my office (принесите мне книгу T-SQL Querying с полки в моем офисе). Обратите внимание, что инструкция начинается не с указания местонахождения объекта (офис), а с самого объекта (книга). Но если задуматься о порядке, в котором необходимо выполнять такую инструкцию, то сначала требуется зайти в комнату, потом найти полку, а затем уже взять книгу и принести ее.

Аналогично порядок ввода предложений запроса начинается с предложений SELECT, указывающих возвращаемые столбцы, следом идет предложение FROM, указывающее входные таблицы и табличные операторы, а затем предложение WHERE с фильтром строк и т. д. Однако логическая обработка запросов должна начинаться с идентификации задействованных таблиц (предложение FROM), затем необходимо применить все фильтры строк (WHERE), сгруппировать (GROUP BY), выполнить групповую фильтрацию (HAVING) и только после этого перейти к столбцам, которые нужно возвратить (SELECT).

После того как вы поймете разницу между порядком ввода предложений запроса и логическим порядком обработки запросов, становятся понятными многие особенности SQL, которые в противном случае кажутся странными.

На рисунке 2 показан требуемый порядок ввода предложений запроса. На рисунке 3 мы видим те же предложения на основе порядка логической обработки запроса.

Порядок логической обработки предложений запроса
Рисунок 3. Порядок логической обработки предложений запроса

На рисунке 4 показана подробная блок-схема логической обработки запроса. Входы — это таблицы, указанные в предложении FROM. Каждый шаг применяется к таблице или таблицам, предоставляемым в качестве входных данных, и возвращает виртуальную таблицу в качестве выходных данных. На самом последнем шаге окончательный результат запроса возвращается приложению или внешнему запросу.

Блок-схема логической обработки запроса
Рисунок 4. Блок-схема логической обработки запроса

Если вам нравится решать задачи, рассмотрите другие шаги в блок-схеме. Впрочем, я буду их подробно разбирать в следующих статьях; пока они представлены для справки. При чтении следующих статей серии держите под рукой рисунки 2, 3 и 4. Они вам пригодятся.

Та же база данных и тестовые запросы

В моих примерах будет использоваться тестовая база данных с именем TSQLV4. Исходный текст, с помощью которого можно создать и заполнить базу данных, находится по адресу: http://tsql.solidq.com/SampleDatabases/TSQLV4.zip. Эта база данных представляет собой простую систему ввода информации о заказах с таблицами Sales.Customers, Sales.Orders (с заголовками заказов), Sales.OrderDetails (со строками заказов) и т. д. База данных совместима со всеми версиями, начиная с SQL Server 2008 и до 2016, а также с базой данных SQL Azure. Перед запуском программного кода из статей данной серии убедитесь, что эта база данных установлена и доступна. После установки используйте следующую команду для переключения контекста на эту базу данных:

USE TSQLV4;

Для объяснения логической обработки запросов будут использоваться два тестовых запроса, простой и сложный. В статье они именуются «простой тестовый запрос» и «сложный тестовый запрос». В листинге 1 показан простой тестовый запрос. Он возвращает данные заказчиков из Испании, разместивших не более трех заказов. Для подходящих заказчиков возвращаются идентификатор (customer ID) и число заказов. Запрос выдает строки, сортированные по числу заказов в возрастающем порядке.

Вероятно, вы привыкли «читать» запрос в соответствии с порядком ввода предложений. Попробуйте прочитать его в соответствии с порядком логической обработки запроса.

  1. FROM: запрос объединяет таблицы Customers и Orders на основании совпадений между идентификаторами customer ID заказчика и заказа. В запросе используется левое внешнее соединение, чтобы сохранить заказчиков, не разместивших заказы.
  2. WHERE: запрос фильтрует только строки, в которых указана страна заказчика — Испания (Spain).
  3. GROUP BY: запрос группирует оставшиеся строки по идентификатору customer ID заказчика.
  4. HAVING: запрос фильтрует только группы заказчиков, имеющие не более трех заказов
  5. SELECT: для остальных групп запрос возвращает идентификатор customer ID заказчика и число заказов, назначая столбцу имя numorders.
  6. Запрос представляет строку результатов, сортированную по numorders.

В листинге 2 приведен сложный тестовый запрос.

Пока я рекомендую распечатать и простой, и сложный запросы и держать их под рукой при чтении следующих статей серии.

Что дальше

Понимание логической обработки запросов — ключ к разработке производительного кода SQL. Кроме того, чрезвычайно важно понимать определенные ограничения языка и уметь искать обходные пути. В следующих статьях серии мы углубимся в подробности, рассматривая отдельные предложения. А пока предлагаю вам домашнее задание — ответить на следующие вопросы:

  1. В чем разница между предложением ON и предложением WHERE?
  2. Существует ли гарантия, что выражения в предложении WHERE будут вычисляться в определенном порядке?
  3. Для чего служат NULL-значения и какие сложности они вносят в язык?
  4. При объединении таблицы с производной таблицей может ли запрос к производной таблице ссылаться на столбцы из другой таблицы в объединении и почему?
  5. Можно ли использовать псевдоним столбца, который был определен в предложении SELECT, в предложении WHERE и почему?
  6. Можно ли использовать псевдоним, который был определен в предложении SELECT, в других выражениях в предложении SELECT и почему?
  7. Можно ли использовать псевдоним, который был определен в предложении SELECT, в предложении ORDER BY и почему?
  8. Как сделать псевдоним столбца доступным для таких предложений, как WHERE, GROUP BY, HAVING, SELECT?
  9. В чем разница между природой результата запроса, когда в запросе имеется предложение ORDER BY представления и когда оно отсутствует?
  10. В чем разница между групповой агрегатной функцией и оконной агрегатной функцией?
  11. Если во внутреннем запросе присутствует предложение ORDER BY, гарантирован ли порядок представления внешнего запроса?
  12. Если в запросе отсутствует предложение ORDER BY представления, существуют ли какие-нибудь обстоятельства, при которых гарантирован порядок представления запроса?

Листинг 1. Простой тестовый запрос

SELECT C.custid, COUNT( O.orderid ) AS numorders
FROM Sales.Customers AS C
  LEFT OUTER JOIN Sales.Orders AS O
    ON C.custid = O.custid
WHERE C.country = N'Spain'
GROUP BY C.custid
HAVING COUNT( O.orderid ) <= 3
ORDER BY numorders;

Листинг 2. Сложный тестовый запрос

SELECT TOP (4) WITH TIES
  C.custid,
  A.custlocation,
  COUNT( DISTINCT O.orderid ) AS numorders,
  SUM( A.val ) AS totalval,
  SUM( A.val ) / SUM( SUM( A.val ) ) OVER() AS pct
FROM Sales.Customers AS C
  LEFT OUTER JOIN
      ( Sales.Orders AS O
          INNER JOIN Sales.OrderDetails AS OD
            ON O.orderid = OD.orderid
            AND O.orderdate >= '20160101' )
    ON C.custid = O.custid
  CROSS APPLY ( VALUES( CONCAT(C.country, N'.' + C.region, N'.' + C.city),
                        OD.qty * OD.unitprice * (1 - OD.discount) )
              ) AS A(custlocation, val)
WHERE A.custlocation IN (N'Spain.Madrid', N'France.Paris', N'USA.WA.Seattle')
GROUP BY C.custid, A.custlocation
HAVING COUNT( DISTINCT O.orderid ) <= 3
ORDER BY numorders;

Аннотация: Рассматриваются общие принципы построения и отработки предложения SELECT. Приводятся правила построения фразы FROM для указания источников данных и фразы WHERE для отбора строк.

Выборка данных

Бескозырка белая, в полоску воротник…

Пионеры смелые спросили напрямик:

«С какого, парень, года, с какого парохода

И на каких морях ты побывал, моряк?»

Фразы предложения SELECT

Предложение SELECT в SQL складывается из фрагментов, которые по примеру лингвистики носят название фраз (clauses). Иногда их удобно называть более общим словом «конструкции» или же словами «часть предложения».

Допустимые в предложении SQL фразы — это: SELECT, FROM, WHERE, GROUP BY, HAVING, CONNECT BY, ORDER BY, PIVOT/UNPIVOT. Общие правила использования фраз в предложении SELECT следующие:

  • В каждом предложении обязаны быть фразы SELECT и FROM. Остальные фразы необязательны.
  • Порядок следования фраз во всех предложениях фиксирован (например, GROUP BY всегда следует за WHERE и FROM, а ORDER BY всегда стоит в конце).
  • Фраза HAVING может употребляться только в дополнении ко GROUP BY.

Начиная с версии 10 в Oracle SQL возможно еще употребление фразы MODEL, которая, однако, стоит особняком от прочих в силу своей синтаксической инородности и нетрадиционности для SQL. Сфера ее применения также специфична — это базы типа «склады данных», data warehouse. Ниже она не рассматривается.

Кроме этого может иметься фраза WITH, которую можно рассматривать как оформительскую (с версии 9) и процедурную (с версии 11.2) надстройку над «традиционным» предложением SELECT. Она рассматривается в соответствующем разделе ниже.

Логический порядок обработки предложения SELECT

Порядок обработки предложения SELECT также фиксирован и почти совпадает с порядком написания фраз в тексте. Исключения составляет (а) фраза SELECT, которая в отличие от написания обрабатывается в последнюю очередь, и (б) фразы CONNECT BY и WHERE, которые обрабатываются в порядке, обратном написанию:

[11-) с версии 11

Указанный порядок — логический. Это значит, что технические планы обработки запросов СУБД может предлагать самые разнообразные, и вовсе не обязательно они будут точно воспроизводить приведенную выше последовательность в каждом отдельном случае. Причина — в чудовищной затратности, которой чревато простое следование вышеприведенной схеме. Однако действие любого плана, фактически предлагаемого Oracle, никогда не будет вступать в противоречие с этой логикой обработки. Иными словами, когда программисту требуется понять, каким может оказаться результат запроса, он вполне вправе положиться на эту общую логическую последовательность обработки и не вдаваться в подробности фактического плана выполнения.

Она основывается на чередовании этапов вычисления (очередная фраза), так что каждый этап принимает какое-то множество данных на входе и вырабатывает множество данных на выходе. За исключением двух крайних случаев: множества данных на входе фразы FROM и окончательного результата на выходе фразы SELECT, — такие множества можно назвать промежуточными результатами вычислений в предложении SELECT. Вот как выглядит логическая схема обработки предложения SELECT, пожалуй самого распространенного вида SELECT … FROM a, b WHERE условие:

В частности, эта схема способна показать, что порядок указания источников данных во фразе FROM никак не сказывается на результате.

Далее приводятся два примера предсказания ответа путем применения техники последовательности промежуточных вычислений.

Пример 1 предложения SELECT

«Выдать в порядке приема на работу всех продавцов с их окладами»:

SELECT   ename, sal
FROM     emp
WHERE    job = 'SALESMAN'
ORDER BY hiredate

(Здесь предполагается, что данные о всех сотрудниках исчерпываются таблицей EMP. В базе, отличной от схемы SCOTT, где не соблюдена ортогонализация данных, это может оказаться и не так).

Промежуточный результат после фраз FROM и WHERE

В силу того что источник данных для запроса в данном случае один, фраза FROM фактически ничего не вычисляет, а просто «берет» данные таблицы EMP из базы. Реальную работу — отсев строк согласно условию — выполняет фраза WHERE:

  EMPNO ENAME      JOB             MGR HIREDATE        SAL      COMM    DEPTNO
------- ---------- --------- --------- --------- --------- --------- ---------
   7499 ALLEN      SALESMAN       7698 20-FEB-81      1600       300        30
   7521 WARD       SALESMAN       7698 22-FEB-81      1250       500        30
   7654 MARTIN     SALESMAN       7698 28-SEP-81      1250      1400        30
   7844 TURNER     SALESMAN       7698 08-SEP-81      1500         0        30
Промежуточный результат после фразы ORDER BY

На множестве строк, полученных от WHERE, фраза ORDER BY наводит порядок:

  EMPNO ENAME      JOB             MGR HIREDATE        SAL      COMM    DEPTNO
------- ---------- --------- --------- --------- --------- --------- ---------
   7499 ALLEN      SALESMAN       7698 20-FEB-81      1600       300        30
   7521 WARD       SALESMAN       7698 22-FEB-81      1250       500        30
   7844 TURNER     SALESMAN       7698 08-SEP-81      1500         0        30
   7654 MARTIN     SALESMAN       7698 28-SEP-81      1250      1400        30

Пожалуйста, дайте порядок выполнения операторов sql запроса или дайте ссылку, где прочитать об этом: что за чем выполняется. Например (с потолка):

SELECT
  t1.id AS id, t1.name AS name, count(*) AS cnt
FROM
  tbl_name1 t1
INNER JOIN
  tbl_name2 t2 ON t1.id = t2.id
WHERE
  t1.id > 10
GROUP BY
  t1.name
HAVING
  cnt > 10
ORDER BY
  t1.id

Сначала GROUP BY, потом? Последним ORDER BY
Нигде не могу найти нормальной информации об этом :((

задан 13 авг 2011 в 6:19

FoxManiac's user avatar

Для SQL Server смотри ссылку.

Логический порядок обработки инструкции SELECT:

  1. FROM
  2. ON
  3. JOIN
  4. where
  5. GROUP BY
  6. WITH CUBE или WITH ROLLUP
  7. HAVING
  8. SELECT
  9. DISTINCT
  10. ORDER BY
  11. TOP

aleksandr barakin's user avatar

ответ дан 29 янв 2016 в 13:06

Юрий's user avatar

ЮрийЮрий

811 серебряный знак1 бронзовый знак

Порядок выполнения зависит от ситуации и определяется перед выполнением запроса. СУБД позволяют просмотреть план выполнения — это то, как именно СУБД решила выполнить запрос.

ответ дан 13 авг 2011 в 7:06

Олег Нечитайло's user avatar

Олег НечитайлоОлег Нечитайло

1,4667 серебряных знаков14 бронзовых знаков

1

ответ дан 13 авг 2011 в 7:10

msi's user avatar

msimsi

11.4k16 серебряных знаков16 бронзовых знаков

5

SELECT 
       [DISTINCT | DISTINCTROW | ALL]
       select_expression,...
   [FROM table_references]
     [WHERE where_definition]
     [GROUP BY {unsigned_integer | col_name | formula} [ASC | DESC], ...]
     [HAVING where_definition]
     [ORDER BY {unsigned_integer | col_name | formula} [ASC | DESC], ...]

ответ дан 13 авг 2011 в 6:53

Avalon's user avatar

AvalonAvalon

1,0186 серебряных знаков14 бронзовых знаков

1

SQL is a declarative language. The result of a query must be what you would get if you evaluated as follows (from Microsoft):

Logical Processing Order of the SELECT statement

The following steps show the logical
processing order, or binding order,
for a SELECT statement. This order
determines when the objects defined in
one step are made available to the
clauses in subsequent steps. For
example, if the query processor can
bind to (access) the tables or views
defined in the FROM clause, these
objects and their columns are made
available to all subsequent steps.
Conversely, because the SELECT clause
is step 8, any column aliases or
derived columns defined in that clause
cannot be referenced by preceding
clauses. However, they can be
referenced by subsequent clauses such
as the ORDER BY clause. Note that the
actual physical execution of the
statement is determined by the query
processor and the order may vary from
this list.

  1. FROM
  2. ON
  3. JOIN
  4. WHERE
  5. GROUP BY
  6. WITH CUBE or WITH ROLLUP
  7. HAVING
  8. SELECT
  9. DISTINCT
  10. ORDER BY
  11. TOP

The optimizer is free to choose any order it feels appropriate to produce the best execution time. Given any SQL query, is basically impossible to anybody to pretend it knows the execution order. If you add detailed information about the schema involved (exact tables and indexes definition) and the estimated cardinalities (size of data and selectivity of keys) then one can take a guess at the probable execution order.

Ultimately, the only correct ‘order’ is the one described ion the actual execution plan. See Displaying Execution Plans by Using SQL Server Profiler Event Classes and Displaying Graphical Execution Plans (SQL Server Management Studio).

A completely different thing though is how do queries, subqueries and expressions project themselves into ‘validity’. For instance if you have an aliased expression in the SELECT projection list, can you use the alias in the WHERE clause? Like this:

SELECT a+b as c
FROM t
WHERE c=...;

Is the use of c alias valid in the where clause? The answer is NO. Queries form a syntax tree, and a lower branch of the tree cannot be reference something defined higher in the tree. This is not necessarily an order of ‘execution’, is more of a syntax parsing issue. It is equivalent to writing this code in C#:

void Select (int a, int b)
{
   if (c = ...) then {...}
   int c = a+b;
}

Just as in C# this code won’t compile because the variable c is used before is defined, the SELECT above won’t compile properly because the alias c is referenced lower in the tree than is actually defined.

Unfortunately, unlike the well known rules of C/C# language parsing, the SQL rules of how the query tree is built are somehow esoteric. There is a brief mention of them in Single SQL Statement Processing but a detailed discussion of how they are created, and what order is valid and what not, I don’t know of any source. I’m not saying there aren’t good sources, I’m sure some of the good SQL books out there cover this topic.

Note that the syntax tree order does not match the visual order of the SQL text. For example the ORDER BY clause is usually the last in the SQL text, but as a syntax tree it sits above everything else (it sorts the output of the SELECT, so it sits above the SELECTed columns so to speak) and as such is is valid to reference the c alias:

SELECT a+b as c
FROM t
ORDER BY c;

Перевод статьи «SQL Order of Operations».

Мы привыкли, что компьютер выполняет команды программиста последовательно, в том порядке, который указал автор кода. Однако SQL относится к декларативным языкам, то есть SQL-запрос описывает ожидаемый результат, а не способ его получения.

Давайте разберём, в какой последовательности выполняются шесть операций в SQL: SELECT, FROM, WHERE, GROUP BY, HAVING и ORDER BY.

База данных выполняет команды в строгой очерёдности, о которой полезно знать любому разработчику. Залог оптимального запроса тот же, что и залог успеха в приготовлении вкусного блюда: важно знать не только ингредиенты, но и когда каждый из них должен попасть в блюдо. Если база данных отойдет от стандартного сценария выполнения команд, то ее производительность может сильно пострадать.

База данных сотрудников

В этой статье мы поработаем с типичной базой сотрудников, относящихся к разным отделам. По каждому сотруднику известны его ID, имя, фамилия, зарплата и отдел:

Таблица EMPLOYEE:

EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY DEPARTMENT
100 James Smith 78,000 ACCOUNTING
101 Mary Sexton 82,000 IT
102 Chun Yen 80,500 ACCOUNTING
103 Agnes Miller 95,000 IT
104 Dmitry Komer 120,000 SALES
EMPLOYEE

Таблица DEPARTMENT:

DEPT_NAME MANAGER BUDGET
ACCOUNTING 100 300,000
IT 101 250,000
SALES 104 700,000
DEPARTMENT

Проанализировать порядок выполнения команд в запросах помогут типичные задачи:

  • Найти имена сотрудников отдела IT
  • Посчитать количество сотрудников каждого отдела с зарплатой выше 80 000.

Начнем с получения имён  сотрудников отдела IT:

SELECT LAST_NAME, FIRST_NAME
  FROM EMPLOYEE
 WHERE DEPARTMENT = 'IT'

В первую очередь выполняется FROM EMPLOYEE:

EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY DEPARTMENT
100 James Smith 78,000 ACCOUNTING
101 Mary Sexton 82,000 IT
102 Chun Yen 80,500 ACCOUNTING
103 Agnes Miller 95,000 IT
104 Dmitry Komer 120,000 SALES

Затем наступает очередь  WHERE DEPARTMENT = ‘IT’, который фильтрует колонку DEPARTMENT:

EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY DEPARTMENT
101 Mary Sexton 82,000 IT
103 Agnes Miller 95,000 IT

Наконец,  SELECT FIRST_NAME, LAST_NAME скрывает ненужные колонки и возвращает финальный результат:

FIRST_NAME LAST_NAME
Mary Sexton
Agnes Miller

Отлично! После первого препарирования выяснилось, что простой запрос с операторами SELECT, FROM, и WHERE выполняется по следующей схеме:

  • FROM (выбор таблицы)
  • WHERE (фильтрация строк)
  • SELECT (возврат результирующего датасета).

Влияние ORDER BY на план выполнения запроса

Допустим, что начальнику не понравился отчет, основанный на предыдущем запросе, потому что он хочет видеть имена в алфавитном порядке. Исправим это с помощью ORDER BY:

SELECT LAST_NAME, FIRST_NAME
    FROM EMPLOYEE
   WHERE DEPARTMENT = 'IT'
ORDER BY FIRST_NAME

Выполняться такой запрос будет так же, как и предыдущий. Только в конце ORDER BY отсортирует строки в алфавитном порядке по колонке FIRST_NAME:

FIRST_NAME LAST_NAME
Agnes Miller
Mary Sexton

Таким образом, команды SELECT, FROM, WHERE и ORDER BY выполняются в следующей последовательности:

  1. FROM (выбор таблицы)
  2. WHERE (фильтрация строк)
  3. SELECT (возврат результирующего датасета)
  4. ORDER BY (сортировка)

GROUP BY и HAVING

Усложним задачу. Посчитаем количество сотрудников каждого отдела с зарплатой выше 80 000 и остортируем результат по убыванию. Нам подойдёт следующий запрос:

SELECT DEPARTMENT, COUNT(*)
    FROM EMPLOYEES
   WHERE SALARY > 80000
GROUP BY DEPARTMENT
ORDER BY COUNT(*) DESC

Как обычно, в первую очередь выполнится FROM EMPLOYEE и вернет сырые данные:

EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY DEPARTMENT
100 James Smith 78,000 ACCOUNTING
101 Mary Sexton 82,000 IT
102 Chun Yen 80,500 ACCOUNTING
103 Agnes Miller 95,000 IT
104 Dmitry Komer 120,000 SALES

После выполнения WHERE SALARY > 80000 выборка сузится:

EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY DEPARTMENT
101 Mary Sexton 82,000 IT
102 Chun Yen 80,500 ACCOUNTING
103 Agnes Miller 95,000 IT
104 Dmitry Komer 120,000 SALES

Затем применяется GROUP BY. При этом генерируется по одной записи для каждого отдельного значения в указанной колонке. В нашем примере мы создаем по одной записи для каждого отдельного значения колонки DEPARTMENT:

DEPARTMENT
ACCOUNTING
IT
SALES

После этого применяется SELECT с COUNT(*), производя промежуточный результат:

DEPARTMENT COUNT(*)
ACCOUNTING 1
IT 2
SALES 1

Применение ORDER BY завершает выполнение запроса и возвращает конечный результат:

DEPARTMENT COUNT(*)
IT 2
ACCOUNTING 1
SALES 1

План выполнения данного запроса следующий:

  1. FROM (выбор таблицы)
  2. WHERE (фильтрация строк)
  3. GROUP BY (агрегирование данных)
  4. SELECT (возврат результирующего датасета)
  5. ORDER BY (сортировка).

Добавим выражение HAVING

HAVING — это аналог WHERE для GROUP BY. С его помощью можно фильтровать агрегированные данные.

Давайте применим HAVING и определим, в каких отделах (за исключением отдела продаж) средняя зарплата сотрудников больше 80 000.

SELECT DEPARTMENT
    FROM EMPLOYEES
   WHERE DEPARTMENT <> 'SALES'
GROUP BY DEPARTMENT
  HAVING AVG(SALARY) > 80000

По уже известной нам схеме сначала выберем все данные из таблицы при помощи FROM EMPLOYEE:

EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY DEPARTMENT
100 James Smith 78,000 ACCOUNTING
101 Mary Sexton 82,000 IT
102 Chun Yen 80,500 ACCOUNTING
103 Agnes Miller 95,000 IT
104 Dmitry Komer 120,000 SALES

Затем конструкция WHERE избавит нас от данных по отделу SALES:

EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY DEPARTMENT
100 James Smith 78,000 ACCOUNTING
101 Mary Sexton 82,000 IT
102 Chun Yen 80,500 ACCOUNTING
103 Agnes Miller 95,000 IT

GROUP BY сгенерирует следующие записи:

DEPARTMENT AVG(SALARY)
ACCOUNTING 79,250
IT 88,500

HAVING AVG(SALARY) > 80000 ограничит список:

DEPARTMENT AVG(SALARY)
IT 88,500

А SELECT вернет финальный результат:

DEPARTMENT
IT

Порядок выполнения для данного запроса следующий:

  1. FROM (выбор таблицы)
  2. WHERE (фильтрация строк)
  3. GROUP BY (агрегирование данных)
  4. HAVING (фильтрация агрегированных данных)
  5. SELECT (возврат результирующего датасета).

Новый оператор — JOIN

До этого момента мы имели дело с одной таблицей. А что если воспользоваться JOIN  и добавить ещё одну? Выясним фамилии и ID сотрудников, работающих в отделе с бюджетом более 275 000:

SELECT EMPLOYEE_ID, LAST_NAME
  FROM EMPLOYEES
  JOIN DEPARTMENT
    ON DEPARTMENT = DEPT_NAME
 WHERE BUDGET > 275000

FROM EMPLOYEE как обычно запрашивает данные из таблицы EMPLOYEES:

EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY DEPARTMENT
100 James Smith 78,000 ACCOUNTING
101 Mary Sexton 82,000 IT
102 Chun Yen 80,500 ACCOUNTING
103 Agnes Miller 95,000 IT
104 Dmitry Komer 120,000 SALES

А теперь JOIN запросит сырые данные из DEPARTMENT и скомбинирует данные двух таблиц по условию ON DEPARTMENT = DEPT_NAME:

EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY DEPARTMENT DEPT_NAME MANAGER BUDGET
100 James Smith 78,000 ACCOUNTING ACCOUNTING 100 300,000
101 Mary Sexton 82,000 IT IT 101 250,000
102 Chun Yen 80,500 ACCOUNTING ACCOUNTING 100 300,000
103 Agnes Miller 95,000 IT IT 101 250,000
104 Dmitry Komer 120,000 SALES SALES 104 700,000

Потом применяем WHERE BUDGET > 275000:

EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY DEPARTMENT DEPT_NAME MANAGER BUDGET
100 James Smith 78,000 ACCOUNTING ACCOUNTING 100 300,000
102 Chun Yen 80,500 ACCOUNTING ACCOUNTING 100 300,000
104 Dmitry Komer 120,000 SALES SALES 104 700,000

SELECT EMPLOYEE_ID, LAST_NAME покажет финальный результат:

EMPLOYEE_ID LAST_NAME
100 Smith
102 Yen
104 Komer

Для этого запроса план выполнения следующий:

  1. FROM (выбор таблицы)
  2. JOIN (комбинация с подходящими по условию данными из второй таблицы)
  3. WHERE (фильтрация строк)
  4. SELECT (возврат результирующего датасета).

Итог

Примеры разных запросов убедительно продемонстрировали, что существует строгий порядок выполнения операций. Но этот порядок может меняться в зависимости от набора команд в запросе. Вот универсальная шпаргалка по очередности выполнения операций в SQL-запросах:

  1. FROM (выбор таблицы)
  2. JOIN (комбинация с подходящими по условию данными из других таблиц)
  3. WHERE (фильтрация строк)
  4. GROUP BY (агрегирование данных)
  5. HAVING (фильтрация агрегированных данных)
  6. SELECT (возврат результирующего датасета)
  7. ORDER BY (сортировка).

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

Понравилась статья? Поделить с друзьями:
  • Логический модуль инструкция lum инструкция
  • Логический модуль elko lum инструкция
  • Логический анализатор la104 инструкция по применению
  • Логически сгруппированная часть исходного кода например набор инструкций
  • Логиста таблетки от давления инструкция по применению взрослым