Я изучаю 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
до его создания?
Может ли кто-нибудь дать представление о том, как это работает?
Заранее благодарю за помощь!
Заказ действителен для одного запроса. WHERE
идет после FROM
, поэтому вы можете использовать псевдоним таблицы c в WHERE
, что вы и делаете. Вы используете его в подзапросе, который также сначала выполняет FROM
, затем WHERE
и так далее.
Вы используете псевдоним FROM
, который стоит перед вашим WHERE
, поэтому, конечно, у него есть доступ.
Это неправильный вопрос. Механизм запросов SQL может строить планы выполнения на основе таких вещей, как метаданные статистики таблиц, индексы и даже текущая незавершенная работа или таблицы, уже загруженные в оперативную память, а также таблицы, которые все еще находятся на диске, способами, которые бросают вызов простому упорядочиванию на основе ключевых слов. Один запрос может предварительно фильтровать таблицу на основе предиката предложения WHERE. Другой запрос может загрузить весь набор в оперативную память, чтобы лучше использовать индекс или раздел, а затем применить предикат WHERE.
@Joel Coehoorn: Что ж, этот порядок исполнения имеет смысл. Это объясняет, почему мы не можем использовать псевдонимы столбцов, определенные в предложении SELECT
, например, в предложении WHERE
. Вы, конечно, правы в том, что СУБД может найти оптимальный план выполнения, но знание общего порядка выполнения предложений в SQL может помочь.
Порядок выполнения не полный и не совсем правильный. Например, FROM
не встречается раньше JOIN
, но СУБД просматривает все задействованные таблицы и решает, с какой начать. Для этого решения используются ON
и WHERE
. Итак, да, вы можете использовать псевдоним таблицы в WHERE
, поскольку он определен в FROM
/JOIN
, но СУБД может просмотреть ON
/WHERE
, чтобы решить, что делать с таблицами.
@ThorstenKettner Все это не имеет значения. Вы оба обсуждаете физическую реализацию, которая зависит от прихотей компилятора, а не вопрос здесь. Логическая обработка (этап синтаксического анализа/лексического анализа) четко определена и действительно выполняется именно в описанном порядке (не считая оговорок в моем ответе).
Это упоминается в документации:
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
.
Про псевдонимы это ничего не говорит...
@LaurenzAlbe Да, «Подзапрос может ссылаться на переменные из окружающего запроса, которые будут действовать как константы во время любой оценки подзапроса». это означает, что любое выражение из внешней области видимости видно во внутренней области, которая включает в себя все столбцы из всех предложений FROM
. Обратите внимание, что это в отличие от нелатерального JOIN
, который этого не допускает, о чем и пытаются сказать документы.
Мой комментарий должен был быть более ясным: ваш ответ не объясняет, почему подзапрос может использовать псевдоним таблицы из окружающего запроса.
Думаю, я поймал тебя с первого раза. Как я уже сказал: судя по этой цитате, я думаю, что да. Все выражения из окружающего запроса находятся в области видимости, включая псевдонимы в FROM
.
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:
Все остальные ключевые слова являются подпредложениями (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)...
Это коррелированный подзапрос, что означает, что он имеет доступ к другому запросу, включая его псевдоним.