15.2.15.9 Латеральные производные таблицы
Производная таблица обычно не может ссылаться (зависить) на столбцы предыдущих таблиц в том же FROM запросе. Производная таблица может быть определена как латеральная производная таблица, чтобы указать, что такие ссылки разрешены.
Нелатеральные производные таблицы задаются с помощью синтаксиса, обсуждаемого в Разделе 15.2.15.8, «Производные таблицы». Синтаксис латеральной производной таблицы такой же, как и для нелатеральной производной таблицы, за исключением того, что ключевое слово LATERAL указывается перед спецификацией производной таблицы. Ключевое слово LATERAL должно предшествовать каждой таблице, используемой как латеральная производная таблица.
Латеральные производные таблицы подчиняются следующим ограничениям:
Латеральная производная таблица может встречаться только в
FROMзапросе, либо в списке таблиц, разделённых запятыми, либо в спецификации соединения (JOIN,INNER JOIN,CROSS JOIN,LEFT [OUTER] JOINилиRIGHT [OUTER] JOIN).-
Если латеральная производная таблица находится в правом операнде предложения соединения и содержит ссылку на левый операнд, операция соединения должна быть
INNER JOIN,CROSS JOINилиLEFT [OUTER] JOIN.Если таблица находится в левом операнде и содержит ссылку на правый операнд, операция соединения должна быть
INNER JOIN,CROSS JOINилиRIGHT [OUTER] JOIN. Если латеральная производная таблица ссылается на агрегатную функцию, запрос агрегации функции не может быть тем, который владеет
FROMзапросом, в котором появляется латеральная производная таблица.В соответствии со стандартом SQL MySQL всегда обрабатывает соединение с функцией таблицы, такой как
JSON_TABLE(), как будто было использованоLATERAL. Поскольку ключевое словоLATERALподразумевается, оно не разрешено передJSON_TABLE(); это также соответствует стандарту SQL.
Ниже приведён пример того, как латеральные производные таблицы позволяют выполнять определённые операции SQL, которые нельзя выполнить с помощью нелатеральных производных таблиц или которые требуют менее эффективных обходных путей.
Предположим, что мы хотим решить эту задачу: учитывая таблицу сотрудников отдела продаж (где каждая строка описывает сотрудника отдела продаж) и таблицу всех продаж (где каждая строка описывает продажу: сотрудник, клиент, сумма, дата), определить размер и клиента самой крупной продажи для каждого сотрудника. Эту задачу можно решить двумя способами.
Первый подход к решению проблемы: для каждого сотрудника вычислить максимальный размер продажи и также найти клиента, который предоставил этот максимум. В MySQL это можно сделать так:
SELECT
salesperson.name,
-- find maximum sale size for this salesperson
(SELECT MAX(amount) AS amount
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id)
AS amount,
-- find customer for this maximum size
(SELECT customer_name
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id
AND all_sales.amount =
-- find maximum size, again
(SELECT MAX(amount) AS amount
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id))
AS customer_name
FROM
salesperson;
Этот запрос неэффективен, потому что он вычисляет максимальный размер дважды для каждого сотрудника (один раз в первом подзапросе и один раз во втором).
Мы можем попытаться получить выигрыш в эффективности, вычислив максимум один раз для каждого сотрудника и “кэшируя” его в производной таблице, как показано в изменённом запросе:
SELECT
salesperson.name,
max_sale.amount,
max_sale_customer.customer_name
FROM
salesperson,
-- calculate maximum size, cache it in transient derived table max_sale
(SELECT MAX(amount) AS amount
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id)
AS max_sale,
-- find customer, reusing cached maximum size
(SELECT customer_name
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id
AND all_sales.amount =
-- the cached maximum size
max_sale.amount)
AS max_sale_customer;
Однако запрос является незаконным в SQL-92, потому что производные таблицы не могут зависеть от других таблиц в том же FROM запросе. Производные таблицы должны быть постоянными на протяжении всего запроса и не содержать ссылок на столбцы других таблиц FROM запроса. В исходном виде запрос выдаёт такую ошибку:
ERROR 1054 (42S22): Unknown column 'salesperson.id' in 'where clause'
В SQL:1999 запрос становится законным, если производные таблицы предваряются ключевым словом LATERAL (что означает “эта производная таблица зависит от предыдущих таблиц слева от неё”):
SELECT
salesperson.name,
max_sale.amount,
max_sale_customer.customer_name
FROM
salesperson,
-- calculate maximum size, cache it in transient derived table max_sale
LATERAL
(SELECT MAX(amount) AS amount
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id)
AS max_sale,
-- find customer, reusing cached maximum size
LATERAL
(SELECT customer_name
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id
AND all_sales.amount =
-- the cached maximum size
max_sale.amount)
AS max_sale_customer;
Латеральная производная таблица не обязательно должна быть постоянной и обновляется каждый раз, когда новая строка из предыдущей таблицы, от которой она зависит, обрабатывается верхним запросом.
Второй подход к решению проблемы: можно использовать другое решение, если подзапрос в списке SELECT может возвращать несколько столбцов:
SELECT
salesperson.name,
-- find maximum size and customer at same time
(SELECT amount, customer_name
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id
ORDER BY amount DESC LIMIT 1)
FROM
salesperson;
Это эффективно, но нелегально. Это не работает, потому что такие подзапросы могут возвращать только один столбец:
ERROR 1241 (21000): Operand should contain 1 column(s)
Одна попытка переписать запрос заключается в выборе нескольких столбцов из производной таблицы:
SELECT
salesperson.name,
max_sale.amount,
max_sale.customer_name
FROM
salesperson,
-- find maximum size and customer at same time
(SELECT amount, customer_name
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id
ORDER BY amount DESC LIMIT 1)
AS max_sale;
Однако и это не работает. Производная таблица зависит от таблицы salesperson и, следовательно, терпит неудачу без LATERAL:
ERROR 1054 (42S22): Unknown column 'salesperson.id' in 'where clause'
Добавление ключевого слова LATERAL делает запрос законным:
SELECT
salesperson.name,
max_sale.amount,
max_sale.customer_name
FROM
salesperson,
-- find maximum size and customer at same time
LATERAL
(SELECT amount, customer_name
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id
ORDER BY amount DESC LIMIT 1)
AS max_sale;
Короче говоря, LATERAL является эффективным решением всех недостатков в двух только что обсуждённых подходах.
© 2025 Oracle
Licensed under the GPLv2 License.