DBAtlas · 数据库运维图谱

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

SQL 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 子聚合实现。这不是相关子查询,而是在单个索引上进行分组加组内采样。

在 DBAtlas 中打开 →