Порядок оценки операторов SQL

Я изучаю PostgreSQL и хочу понять точный порядок вычисления операторов SQL. После исследования я нашел следующий порядок оценки:

 1. FROM 
 2. ON 
 3. JOIN 
 4. WHERE 
 5. GROUP BY 
 6. HAVING
 7. SELECT 
 8. AS  
 9. DISTINCT 
 10. ORDER BY 
 11. LIMIT / OFFSET   

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

SELECT first_name, last_name
    FROM customer AS c
    WHERE EXISTS(
    (SELECT * FROM payment AS p
    WHERE p.customer_id = c.customer_id
    AND amount > 11)
    );

Как подзапрос может использовать псевдоним c и как подзапрос получает доступ к псевдониму c до его создания?

Может ли кто-нибудь дать представление о том, как это работает?

Заранее благодарю за помощь!

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

Adrian Klaver 22.07.2024 23:21

Заказ действителен для одного запроса. WHERE идет после FROM, поэтому вы можете использовать псевдоним таблицы c в WHERE, что вы и делаете. Вы используете его в подзапросе, который также сначала выполняет FROM, затем WHERE и так далее.

Thorsten Kettner 22.07.2024 23:44

Вы используете псевдоним FROM, который стоит перед вашим WHERE, поэтому, конечно, у него есть доступ.

Eric 22.07.2024 23:51

Это неправильный вопрос. Механизм запросов SQL может строить планы выполнения на основе таких вещей, как метаданные статистики таблиц, индексы и даже текущая незавершенная работа или таблицы, уже загруженные в оперативную память, а также таблицы, которые все еще находятся на диске, способами, которые бросают вызов простому упорядочиванию на основе ключевых слов. Один запрос может предварительно фильтровать таблицу на основе предиката предложения WHERE. Другой запрос может загрузить весь набор в оперативную память, чтобы лучше использовать индекс или раздел, а затем применить предикат WHERE.

Joel Coehoorn 22.07.2024 23:51

@Joel Coehoorn: Что ж, этот порядок исполнения имеет смысл. Это объясняет, почему мы не можем использовать псевдонимы столбцов, определенные в предложении SELECT, например, в предложении WHERE. Вы, конечно, правы в том, что СУБД может найти оптимальный план выполнения, но знание общего порядка выполнения предложений в SQL может помочь.

Thorsten Kettner 22.07.2024 23:57

Порядок выполнения не полный и не совсем правильный. Например, FROM не встречается раньше JOIN, но СУБД просматривает все задействованные таблицы и решает, с какой начать. Для этого решения используются ON и WHERE. Итак, да, вы можете использовать псевдоним таблицы в WHERE, поскольку он определен в FROM/JOIN, но СУБД может просмотреть ON/WHERE, чтобы решить, что делать с таблицами.

Thorsten Kettner 23.07.2024 00:00

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

Charlieface 23.07.2024 11:59
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
2
7
70
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Это упоминается в документации:

4.2.11. Скалярные подзапросы

Скалярный подзапрос — это обычный SELECT запрос в круглых скобках, который возвращает ровно одну строку с одним столбцом. .... Запрос SELECT выполняется, и единственное возвращаемое значение используется в выражении окружающего значения. .... Подзапрос может ссылаться на переменные из окружающего запроса, которые будут действовать как константы во время любой оценки подзапроса. См. также раздел 9.23 для других выражений, включающих подзапросы.

В разделе 9.23.1 EXISTS тоже самое написано.

Также обратите внимание:

7.2.1.5.LATERAL Подзапросы

Подзапросам, появляющимся в FROM, может предшествовать ключевое слово LATERAL. Это позволяет им ссылаться на столбцы, предоставленные предыдущими элементами FROM. (Без LATERAL каждый подзапрос оценивается независимо и поэтому не может ссылаться на какой-либо другой элемент FROM.)

Это не то же самое, что скалярный подзапрос. Скалярный подзапрос используется вместо одного значения в выражении и должен возвращать только одну строку (или ни одной). В то время как боковое соединение может возвращать несколько строк и присоединяется к предыдущим ссылкам на таблицу, как обычно.


Кстати, ваш список порядка оценки неточен. SELECT и AS по сути одно и то же. ON и JOIN тоже. Вам также не хватает оконных функций, которые идут после HAVING, а также операторов установки UNION и им подобных, которые идут после DISTINCT, но перед ORDER BY.

Про псевдонимы это ничего не говорит...

Laurenz Albe 23.07.2024 08:06

@LaurenzAlbe Да, «Подзапрос может ссылаться на переменные из окружающего запроса, которые будут действовать как константы во время любой оценки подзапроса». это означает, что любое выражение из внешней области видимости видно во внутренней области, которая включает в себя все столбцы из всех предложений FROM. Обратите внимание, что это в отличие от нелатерального JOIN, который этого не допускает, о чем и пытаются сказать документы.

Charlieface 23.07.2024 11:46

Мой комментарий должен был быть более ясным: ваш ответ не объясняет, почему подзапрос может использовать псевдоним таблицы из окружающего запроса.

Laurenz Albe 23.07.2024 11:55

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

Charlieface 23.07.2024 11:56

c — это псевдоним таблицы, поэтому он принадлежит к предложению FROM, поэтому имеет смысл, что он доступен в подзапросе в WHERE.

Рассмотрим следующий запрос:

SELECT t.c AS x
FROM (VALUES (1), (2)) AS t(c);

x — псевдоним столбца, t и c — псевдонимы таблицы.

Это будет работать:

SELECT t.c AS x
FROM (VALUES (1), (2)) AS t(c)
WHERE EXISTS (SELECT WHERE t.c = 1);

Но это не будет:

SELECT t.c AS x
FROM (VALUES (1), (2)) AS t(c)
WHERE EXISTS (SELECT WHERE x = 1);
Ответ принят как подходящий

Логический порядок стандартных предложений ISO SQL для оператора SQL SELECT:

  1. ОТ
  2. ГДЕ
  3. ГРУППА ПО
  4. ИМЕЮЩИЙ
  5. ОКНО
  6. ВЫБИРАТЬ
  7. СОРТИРОВАТЬ ПО

Все остальные ключевые слова являются подпредложениями (JOIN/ON, DISTINCT, OFFSET/FETCH...) или операторами (IN, BETWEEN, EXISTS...) в зависимости от предложений SQL.

Вопреки тому, что говорит @Laurenz_Albe, ключевое слово AS вводит псевдоним, то есть новое имя, для столбца или таблицы, которое можно использовать в предложениях, расположенных после него, в логическом порядке. Таким образом, AS, определяющий псевдоним для столбца предложения SELECT, может использоваться только в предложении ORDER BY и ни в коем случае в других из следующих элементов предложения SELECT.

Так :

SELECT COL1 AS X, X + 1

Должен выдать исключение.

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

Так :

SELECT *
FROM   Tab1 AS T
       JOIN Tab2 AS U
          ON T.id = U.id

...верно

Запрос, заданный @Laurenz_Albe:

SELECT t.c AS x
FROM (VALUES (1), (2)) AS t(c)
WHERE EXISTS (SELECT WHERE t.c = 1);

... является синтаксически ложным в SQL и не может быть принят ни одной СУБД, кроме PostGreSQL. Потому что внутреннее предложение SELECT должно содержать почти элемент. Правильный запрос может быть:

SELECT t.c AS x
FROM (VALUES (1), (2)) AS t(c)
WHERE EXISTS (SELECT * WHERE t.c = 1);

Избегайте писать такие глупые запросы, которые синтаксически неприемлемы...

Кстати,

Ключевые слова ON и PARTITIONNED зависят от JOIN, которое зависит от предложения FROM, например LATERAL { CROSS | OUTER } APPLY.

ключевое слово DISTINCT, которое является оператором, зависит от предложения FROM и должно предшествовать любому элементу этого предложения.

Ключевое слово LIMIT не является частью стандарта ISO SQL.

Ключевое слово OFFSET зависит от предложения ORDER BY, а FETCH зависит от OFFSET.

Но не путайте логический порядок и физическую обработку запроса. Запрос — это просто предложение, переведенное в математические формулы реляционной алгебры, и эти формулы могут быть:

  • упрощенный
  • переписан

В качестве основного примера упрощения:

SELECT *
FROM   T
WHERE  Col1 + 3 = 5

будет переписано как:

SELECT *
FROM   T
WHERE  Col1 = 2

Более сложное упрощение путем переписывания может быть:

SELECT COUNT(*), PrimaryKeyCol, Col2, Col3, Col4, Col5 
FROM   T
GROUP  BY PrimaryKeyCol, Col2, Col3, Col4, Col5 

... это будет переписано как::

SELECT COUNT(*), PrimaryKeyCol, Col2, Col3, Col4, Col5 
FROM   T
GROUP  BY PrimaryKeyCol 

В зависимости от уровня "интеллекта" оптимизатора (тупо называемого планировщиком в PostGreSQL)...

Другие вопросы по теме