LATERAL / CROSS APPLY (построчный подзапрос)
Oracle
-- Each customer's 3 most recent orders (LATERAL, 12c+)
SELECT m.name, s.created_at, s.amount
FROM customer m,
LATERAL (SELECT created_at, amount FROM orders
WHERE customer_id = m.id
ORDER BY created_at DESC FETCH FIRST 3 ROWS ONLY) s;
-- Alternative: CROSS APPLY (12c+)
SELECT m.name, s.amount
FROM customer m
CROSS APPLY (SELECT amount FROM orders WHERE customer_id = m.id) s;LATERAL/APPLY позволяет подзапросу в FROM ссылаться на столбцы таблицы слева, поэтому можно писать коррелированные соединения вроде «первые N строк для каждой строки». Oracle 12c поддерживает и синтаксис ANSI LATERAL, и CROSS/OUTER APPLY.
PostgreSQL
SELECT m.name, s.created_at, s.amount
FROM customer m
CROSS JOIN LATERAL (
SELECT created_at, amount FROM orders
WHERE customer_id = m.id
ORDER BY created_at DESC LIMIT 3
) s;В PostgreSQL подзапросы, зависящие от строки слева, пишутся с ключевым словом LATERAL (JOIN LATERAL / CROSS JOIN LATERAL / LEFT JOIN LATERAL). Очень удобно для «первых N в группе» и развёртывания строк функциями, возвращающими множества. Синтаксиса CROSS APPLY нет; используется LATERAL.
MySQL
-- MySQL 8.0.14+ supports LATERAL
SELECT m.name, s.amount
FROM customer m
JOIN LATERAL (
SELECT amount FROM orders
WHERE customer_id = m.id
ORDER BY created_at DESC LIMIT 3
) s ON true;С MySQL 8.0.14 поддерживаются производные таблицы LATERAL; подзапрос может ссылаться на столбцы внешней таблицы. До этого, а также для CROSS APPLY, эквивалента не было; «первые N в группе» решались оконными функциями или коррелированными подзапросами. В MariaDB LATERAL пока нет.
SQL Server
SELECT m.name, s.created_at, s.amount
FROM customer m
CROSS APPLY (
SELECT TOP 3 created_at, amount FROM orders
WHERE customer_id = m.id
ORDER BY created_at DESC
) s;
-- Use OUTER APPLY to return non-matching rows tooЭквивалент LATERAL в SQL Server — оператор APPLY: CROSS APPLY (как INNER JOIN LATERAL, отбрасывает несовпадения) и OUTER APPLY (как LEFT JOIN LATERAL, NULL для несовпадений). Обычно используется для вызова табличных функций для каждой строки и запросов «первые N в группе».
MongoDB
// Closest equivalent: $lookup with a sub-pipeline
db.customer.aggregate([
{ $lookup: {
from: "orders",
let: { mid: "$id" },
pipeline: [
{ $match: { $expr: { $eq: ["$customer_id", "$$mid"] } } },
{ $sort: { created_at: -1 } }, { $limit: 3 }
],
as: "last3"
} }
])В MongoDB эквивалент LATERAL/APPLY — форма $lookup с вложенным конвейером и переменными let: поля внешнего документа передаются во вложенный конвейер через let как $$variable, где выполняются сопоставление/сортировка/ограничение. Так решается паттерн «связанные первые N для каждого документа».
Redis
# Not applicable: Redis has no subqueries / LATERAL.Redis не предлагает построчных коррелированных подзапросов. Паттерн «связанные первые N для каждого элемента» решается циклом на стороне приложения (напр. отдельный ZRANGE на каждый ключ) или серверным Lua-скриптом.
Elasticsearch
# Closest equivalent: terms + top_hits sub-aggregation
GET orders/_search
{
"size": 0,
"aggs": { "by_customer": { "terms": { "field": "customer_id" },
"aggs": { "last3": { "top_hits": {
"sort": [ { "created_at": "desc" } ], "size": 3 } } } } }
}В Elasticsearch нет LATERAL/APPLY; паттерн «первые N записей в группе» достигается вложенной агрегацией top_hits внутри агрегации terms. Вместо коррелированного подзапроса это группировка плюс выборка внутри группы по одному индексу.