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 tooSQL Server 中 LATERAL 的等价物是 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 的等价物是带子管道和 let 变量的 $lookup 形式:外部文档的字段通过 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 条记录”模式通过嵌套在 terms 聚合中的 top_hits 子聚合实现。这不是相关子查询,而是在单个索引上进行分组加组内采样。