半/反连接、LATERAL 与流式查询

共 72 题
📑 题目列表 72 题
#
★★★

1. 反连接的执行算法,Hash Anti Join、Merge Anti Join、Bitmap Anti Join?

反连接(Anti Join)有哪些常见执行算法?Hash、Merge、Bitmap Anti Join 各自如何实现?

  • Hash Anti Join
  • Merge Anti Join
  • Bitmap Anti Join

反连接(Anti Join)返回左表无匹配的行,实现算法:Hash Anti Join(构建右表哈希,左表查找无匹配);Merge Anti Join(两表排序后比较);Bitmap Anti Join(用位图标记存在)。优化器按数据选择。

反连接算法高效实现 NOT EXISTS/NOT IN。

#
★★★

2. 反连接(ANTI JOIN)的语义,返回左表中在右表无匹配的行?

反连接(ANTI JOIN)的语义是什么?它返回哪些行,与 NOT EXISTS / NOT IN 子查询有何等价关系?

  • 反连接
  • 无匹配行
  • 等价

反连接(ANTI JOIN)返回左表中在右表无匹配的行,等价于 NOT EXISTS / NOT IN(无 NULL 时)子查询。每行只出现一次。

反连接返回左表在右表无匹配的行,是 NOT EXISTS 的优化形态;与半连接互为镜像,答题时对比两者(一个有匹配、一个无匹配)并说明与 NOT IN 在 NULL 处理上的差异,能体现理解的深度。

#
★★★

3. MySQL 中如何识别半连接与反连接的执行计划(EXPLAIN 中的 FirstMatch、LooseScan)?

说明 MySQL 识别半连接/反连接的执行计划?

  • FirstMatch
  • LooseScan
  • EXPLAIN

MySQL 的 EXPLAIN 中半连接优化类型显示为 FirstMatch、LooseScan、Materialize、Duplicate Weedout 等策略。反连接常显示为 NOT EXISTS。通过阅读 access_type 与 Extra 识别。

熟悉这些策略名有助于判断子查询优化是否生效。

#
★★★

4. NOT EXISTS 与 NOT IN 在 NULL 上的语义差异?

说明 NOT EXISTS 与 NOT IN 在 NULL 上的差异?

  • NOT IN 的 NULL 陷阱
  • NOT EXISTS 不受影响
  • 语义

NOT IN 在子查询结果含 NULL 时,整个 NOT IN 条件为 NULL(永假),导致不返回任何行(错误);NOT EXISTS 只判断是否存在行,不受 NULL 影响。因此含 NULL 时 NOT EXISTS 更安全。

NOT IN 的 NULL 陷阱是经典坑,需避免或处理 NULL。

#
★★★

5. PostgreSQL 中 Semi Join 的优化策略?

说明 PostgreSQL 的半连接优化策略?

  • Semi Join
  • Hash Semi
  • Merge Semi

PostgreSQL 把 EXISTS/IN 子查询优化为半连接(Semi Join),实现策略包括 Hash Semi Join、Merge Semi Join、Nested Loop Semi。EXPLAIN 中显示 "Semi Join" 或 "Hash Semi Join"。

半连接在 EXPLAIN 中有明确标识,可确认优化。

#
★★★

6. Anti Join 与 NOT IN 的等价场景?

说明 Anti Join 与 NOT IN 的等价场景?

  • 等价条件
  • 无 NULL
  • 优化

当子查询结果不含 NULL 时,Anti Join 与 NOT IN 等价。含 NULL 时 NOT IN 语义特殊(全空),优化器可能转为 Anti Join 但需处理 NULL。保证子查询无 NULL 时两者等价。

NOT IN 遇 NULL 结果恒为空,只有子查询无 NULL 时才能安全转为反连接;这是经典 NULL 陷阱,答题要点是无 NULL 才等价,并提醒可用 NOT EXISTS 规避。

#
★★★

7. Bitmap Semi Join 的应用场景?

说明 Bitmap Semi Join 的应用场景?

  • Bitmap
  • 位图
  • 半连接

Bitmap Semi Join 用位图(bitmap)标记右表存在性,用于半连接/反连接判断。适用于可快速构建位图、判断存在性的场景。提供一种高效的 Semi Join 实现。

位图方法空间小、判断快,适合大表存在性判断。

#
★★★

8. INNER JOIN 与 SEMI JOIN 的本质差异?

说明 INNER JOIN 与 SEMI JOIN 的本质差异?

  • 行重复
  • SEMI 去重
  • 语义

INNER JOIN 返回匹配行,右表多行匹配时左表行会重复;SEMI JOIN 只返回左表在右表有匹配的行,每行只出现一次(不重复)。SEMI 关注"是否存在",INNER 关注"连接结果"。

SEMI JOIN 隐式去重,是 INNER JOIN 与 DISTINCT 的替代。

#
★★★

9. LEFT JOIN + IS NULL 与 NOT EXISTS 的等价?

说明 LEFT JOIN + IS NULL 与 NOT EXISTS 的等价?

  • 等价转换
  • 反连接
  • 优化器

LEFT JOIN t2 ON ... WHERE t2.id IS NULL 等价于 NOT EXISTS(找左表无匹配行)。优化器常将两者转为反连接。LEFT JOIN 写法可读但需注意 NULL 语义。

两者都表达反连接,优化器常转成相同计划。

SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id WHERE t2.id IS NULL;
-- 等价于
SELECT * FROM t1 WHERE NOT EXISTS (SELECT 1 FROM t2 WHERE t2.id=t1.id);
#
★★★

10. MySQL 8.0 的 Semi Join 优化(FirstMatch、Duplicate Weedout)?

说明 MySQL 8.0 的半连接优化?

  • FirstMatch
  • Duplicate Weedout
  • 半连接

MySQL 的半连接优化策略包括 FirstMatch(第一个匹配即停止)、Duplicate Weedout(去重)、LooseScan(松散扫描)、Materialize(物化)。优化器自动选择,避免 IN 子查询重复扫描。

这些策略提升 IN/EXISTS 子查询性能。

#
★★★

11. NOT EXISTS 与 LEFT JOIN 的性能对比?

说明 NOT EXISTS 与 LEFT JOIN 的性能对比?

  • 执行计划
  • 反连接
  • 性能

优化器可能将两者转为相同反连接计划,性能接近。但 NOT EXISTS 语义更明确,LEFT JOIN 写法在右表列上过滤时可能退化。通常写法影响不大,关键看优化器是否识别为反连接。

性能取决于优化器;可读性上 NOT EXISTS 更清晰。

#
★★★

12. PostgreSQL 中 Anti Join 的实现策略?

说明 PostgreSQL 中反连接的实现策略?

  • Hash Anti
  • Merge Anti
  • Nested Loop Anti

PostgreSQL 反连接实现策略:Hash Anti Join(构建右表哈希,查找无匹配)、Merge Anti Join(排序后比较)、Nested Loop Anti Join。EXPLAIN 显示 "Anti Join" 节点。

反连接是优化器对 NOT EXISTS/NOT IN 的转换。

#
★★★

13. PostgreSQL 中 Semi Join 的 EXPLAIN 输出解读?

说明 PostgreSQL 半连接的 EXPLAIN 输出?

  • Semi Join 节点
  • 解读
  • 计划

EXPLAIN 中显示 "Semi Join"、内层为 "Hash Semi Join" 或 "Merge Semi Join" 等。读到 "Semi Join" 说明 EXISTS/IN 被优化为半连接,避免重复行。

能识别 Semi Join 节点是判断优化是否生效的关键。

#
★★★

14. SELECT a FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t1.id=t2.id) 的语义?

说明 EXISTS 相关子查询的语义?

  • 相关子查询
  • EXISTS
  • 半连接

该查询对 t1 每行检查 t2 中是否存在 id 相同的行,存在则返回该行。等价于半连接,优化器转为 Semi Join。只判断存在性,不返回 t2 数据。

EXISTS 只判断存在性,等价于半连接,不返回 t2 数据。

SELECT a FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t1.id=t2.id);
#
★★★

15. Semi Join 与 IN 子查询的等价证明?

说明 Semi Join 与 IN 子查询的等价?

  • IN 子查询
  • 半连接
  • 等价

IN 子查询(WHERE t1.id IN (SELECT t2.id FROM t2))等价于半连接:返回 t1 中 id 在 t2 存在的行。优化器将 IN 转为半连接执行。注意 IN 含 NULL 时的语义差异。

IN 与半连接等价,NULL 处理需注意。

#
★★★

16. 反连接的优化器选择(Hash Anti vs Nested Loop Anti)?

说明优化器如何按代价选择 Hash Anti、Nested Loop Anti、Merge Anti 等反连接算法?

  • Hash Anti
  • Nested Loop Anti
  • 代价

优化器按代价选择反连接算法:右表可建哈希且需大量匹配时用 Hash Anti;右表小且左表有索引内表时用 Nested Loop Anti;已排序用 Merge Anti。统计信息影响选择。

优化器根据表大小、索引与统计信息按代价选择反连接算法:哈希适合大表大量匹配,嵌套循环适合小表带索引,已排序数据用归并;答出代价驱动这一原则并举例说明即可覆盖面试追问。

#
★★★

17. LATERAL JOIN 与相关子查询的等价关系?

说明 LATERAL JOIN 与相关子查询的等价?

  • LATERAL
  • 相关子查询
  • 等价

LATERAL 子查询是相关子查询的 FROM 形式,能引用外层表的列。许多相关子查询可改写为 LATERAL,语义等价但写法更清晰。

LATERAL 是相关子查询的 FROM 形式,语义等价且写法更清晰。

SELECT * FROM t1, LATERAL (SELECT * FROM t2 WHERE t2.id = t1.id) sub;
#
★★★

18. LATERAL 与普通子查询的根本差异,是否引用外层表的列?

说明 LATERAL 与普通子查询的根本差异?

  • 引用外层列
  • 相关子查询
  • 差异

根本差异:LATERAL 子查询可以引用外层 FROM 的列(相关),普通子查询不能。LATERAL 对外层每行求值一次,实现逐行关联计算。

能否引用外层列是判断 LATERAL 的关键。

#
★★★

19. LATERAL 关键字的语义,FROM 子句中的相关子查询(每行计算)?

说明 LATERAL 关键字的语义?

  • FROM 子查询
  • 每行计算
  • 相关

LATERAL 关键字允许 FROM 子句中的子查询引用其左侧表/子查询的列,对外层每一行计算一次。语法为 FROM t1, LATERAL (SELECT ...) 或 LEFT JOIN LATERAL。

LATERAL 允许子查询引用外层列,对外层每行求值一次。

SELECT * FROM t1
LEFT JOIN LATERAL (SELECT * FROM t2 WHERE t2.id = t1.id LIMIT 1) sub ON true;
#
★★★

20. LATERAL 在 PostgreSQL、MySQL 8.0.14+、Oracle 12c+ 中的支持情况?

说明 LATERAL 在各数据库的支持情况?

  • PostgreSQL 支持
  • MySQL 8.0.14+
  • Oracle 12c+

PostgreSQL 长期支持 LATERAL;MySQL 8.0.14+ 支持 LATERAL;Oracle 12c+ 支持(及 CROSS/OUTER APPLY)。SQL Server 用 APPLY 等价。了解各版本支持有助于兼容。

方言差异,LATERAL 与 APPLY 等价。

#
★★★

21. LATERAL 的常见用例,取每组 Top N、JSON 展开、相关标量子查询?

说明 LATERAL 的常见用例?

  • 每组 Top N
  • JSON 展开
  • 相关标量子查询

LATERAL 常见用例:取每组 Top N(对每个分组取前 N 条)、JSON 展开(每行展开 JSON 数组)、相关标量子查询(逐行计算)。LATERAL 让这些逐行计算更灵活。

LATERAL 使逐行相关的子查询可复用,是数据逐行处理的利器。

-- 每组 Top N
SELECT * FROM dept d
LEFT JOIN LATERAL (
  SELECT * FROM emp e WHERE e.dept_id = d.id ORDER BY e.salary DESC LIMIT 3
) top ON true;
#
★★★

22. CROSS APPLY 与 LATERAL INNER JOIN 的等价?

说明 CROSS APPLY 与 LATERAL INNER JOIN 的等价?

  • CROSS APPLY
  • LATERAL
  • 等价

SQL Server 的 CROSS APPLY 等价于 LATERAL INNER JOIN(有匹配才返回);OUTER APPLY 等价于 LATERAL LEFT JOIN。两者语义对应。

APPLY 是 SQL Server 的 LATERAL 实现。

#
★★★

23. LATERAL 与 GROUP BY 的协同?

说明 LATERAL 与 GROUP BY 的协同?

  • LATERAL 聚合
  • GROUP BY
  • 协同

LATERAL 子查询中可对每行做聚合(如每组总和),再与主表连接,避免先连接后聚合的扇出。LATERAL 内部可用 GROUP BY/聚合函数。

LATERAL 预聚合是避免扇出的有效手段。

#
★★★

24. LATERAL 与 LIMIT 的协同?

说明 LATERAL 与 LIMIT 的协同?

  • 每组 LIMIT
  • Top N
  • 协同

LATERAL 子查询内可用 LIMIT 实现每组取前 N 行(Top N),这是 LATERAL 的经典用途。对外层每行执行带 LIMIT 的子查询。

LATERAL 内 LIMIT 实现每组取前 N 行,是经典 Top N 写法。

... LATERAL (SELECT * FROM t2 WHERE t2.id=t1.id ORDER BY t2.x LIMIT 1) sub
#
★★★

25. LATERAL 与 OFFSET 分页的取舍?

说明 LATERAL 与 OFFSET 分页的取舍?

  • OFFSET 深分页慢
  • LATERAL 每组
  • 取舍

OFFSET 深分页会扫描大量行,性能差;LATERAL 适合每组内分页(如每组的 Top N)。对全局分页,OFFSET 不如 keyset 分页。LATERAL 用于"每组取 N"而非全局深分页。

深分页用 keyset,每组取 N 用 LATERAL。

#
★★★

26. ORDER BY 与 LIMIT 的协同,Top-N 优化(TopN Heap Sort)?

说明 ORDER BY 与 LIMIT 的 Top-N 优化?

  • Top-N
  • 堆排序
  • 优化

ORDER BY + LIMIT 时,优化器可用 Top-N 优化(TopN Heap Sort),只保留前 N 行而不全量排序,显著降低排序开销。PostgreSQL 用 Top-N 堆排序,MySQL 用 filesort 优化。

全排序被 Top-N 替代,减少内存与 CPU。

#
★★★

27. 排序算法,内存排序 vs 磁盘归并排序(External Merge Sort)的工作原理?

说明内存排序与磁盘归并排序?

  • 内存排序
  • 外部归并
  • 磁盘 I/O

排序数据能放进内存时用快速排序(内存排序);超出内存时用外部归并排序(External Merge Sort):分块排序到磁盘,再多路归并。磁盘归并减少内存占用但增加 I/O。

排序代价由内存与磁盘 I/O 决定:数据能装内存时用快排,超出内存则外部归并,以 I/O 换内存;理解 External Merge Sort 的分块归并,才能解释增大 work_mem 加速大排序。

#
★★

28. MySQL 中 LATERAL 的限制(IN/EXISTS 子查询中不能引用外层)?

说明 MySQL 中 LATERAL 的限制?

  • 限制
  • 引用
  • 版本

MySQL 8.0.14+ 的 LATERAL 子查询在 FROM 中可引用外层列,但在函数/标量上下文(如 IN/EXISTS 子查询)中不能直接引用外层列。LATERAL 仅用于 FROM 子句。

LATERAL 的使用范围受限,需在 FROM 中。

#
★★

29. OUTER APPLY 与 LATERAL LEFT JOIN 的等价?

说明 OUTER APPLY 与 LATERAL LEFT JOIN 的等价?

  • OUTER APPLY
  • LATERAL LEFT JOIN
  • 等价

SQL Server 的 OUTER APPLY 等价于 LATERAL LEFT JOIN(无匹配也返回左表行,填 NULL)。CROSS APPLY 对应 LATERAL INNER JOIN。

OUTER APPLY 保留左表所有行。

#
★★

30. PostgreSQL LATERAL 的优化技巧?

说明 PostgreSQL LATERAL 的优化技巧?

  • 索引
  • 子查询
  • 优化

LATERAL 优化技巧:子查询内建索引(外层每行用索引查找)、限制子查询结果(LIMIT)、避免不必要的相关条件、用 LEFT JOIN LATERAL 避免丢失行。确保内层走索引。

LATERAL 逐行执行,内层索引至关重要。

#
★★

31. SELECT t1., sub. FROM t1, LATERAL (SELECT * FROM t2 WHERE t2.id=t1.id LIMIT 1) sub 的语义?

说明该 LATERAL 查询的语义?

  • 每行取一条
  • 省略 ON
  • 语义

该查询对 t1 每行,在 t2 中取 id 匹配的第一条(LIMIT 1),返回 t1 全部列与 sub 的列。省略 ON 的逗号 + LATERAL 等价于 CROSS JOIN LATERAL(有匹配才返回,无匹配该行丢弃)。

无 ON 的 LATERAL 是内连接风格,无匹配行被丢弃。

#
★★

32. ORDER BY 的执行阶段,在 SELECT 投影后、最终返回前的全排序代价?

说明 ORDER BY 的执行阶段与代价?

  • 排序阶段
  • 全排序
  • 代价

ORDER BY 在 SELECT 投影后、返回前对结果集排序,除非走索引或 Top-N 优化,否则需全量排序,代价高(内存/磁盘 I/O)。排序在最终返回前执行。

全排序是 ORDER BY 的高成本点,需索引或 Top-N 缓解。

#
★★

33. 索引排序(Index Scan)与显式排序的取舍,ORDER BY 命中索引时的零成本?

说明索引排序与显式排序的取舍?

  • 索引排序
  • 显式排序
  • 取舍

ORDER BY 列命中索引时,可用索引有序扫描(Index Scan)直接按序输出,无需显式排序,成本接近零。否则需显式排序。取舍:索引加速排序但增加维护成本。

ORDER BY 列命中索引时可用有序扫描免去显式排序,成本近乎为零,但索引本身有写入与存储开销;答题核心是用索引换排序、以写成本换读性能的取舍,覆盖索引则进一步免去回表。

#
★★

34. DISTINCT ON (col) 在 PostgreSQL 的语义?

说明 PostgreSQL 的 DISTINCT ON?

  • DISTINCT ON
  • 每组第一行
  • 需 ORDER BY

DISTINCT ON (col) 返回每个 distinct col 值对应的第一行(按 ORDER BY 排序后的第一条)。需配合 ORDER BY 指定取哪一行。是 Postgres 特有语法。

DISTINCT ON 返回每组第一行,需 ORDER BY 指定取舍。

SELECT DISTINCT ON (dept_id) * FROM emp ORDER BY dept_id, salary DESC;
#
★★

35. DISTINCT 与 SELECT * 的兼容性?

说明 DISTINCT 与 SELECT * 的兼容性?

  • SELECT *
  • DISTINCT
  • 全列去重

SELECT DISTINCT * 对所有列去重,即整行完全相同的才会去重。若某列(如主键)唯一,则 DISTINCT 无效果。SELECT * 与 DISTINCT 兼容但去重粒度是整行。

SELECT DISTINCT * 的去重粒度是整行,任一列不同就不会去重,含主键时 DISTINCT 完全失效;理解去重粒度才能解释看似去重却无用的现象,这也是少用 SELECT * 的原因之一。

#
★★

36. DISTINCT 的索引优化?

说明 DISTINCT 的索引优化?

  • 索引去重
  • 有序扫描
  • 优化

DISTINCT 列若在索引中,可用索引有序扫描去重(相邻相同值合并),避免哈希去重/排序。覆盖索引可加速 DISTINCT。

索引使 DISTINCT 高效,避免额外排序。

#
★★

37. GROUP BY 与 ORDER BY 的协同?

说明 GROUP BY 与 ORDER BY 的协同?

  • 聚合后排序
  • ORDER BY 聚合列
  • 协同

GROUP BY 先分组聚合,ORDER BY 对聚合结果排序。ORDER BY 只能引用分组列或聚合列(如 SUM(x))。两者协同实现"分组后排序"。

ORDER BY 只能引用分组列或聚合列,实现分组后排序。

SELECT dept, COUNT(*) FROM emp GROUP BY dept ORDER BY COUNT(*) DESC;
#
★★

38. LIMIT 与 OFFSET 在排序后的应用?

说明 LIMIT 与 OFFSET 在排序后的应用?

  • 先排序后截取
  • 分页
  • 深分页

逻辑上先 ORDER BY 排序,再 LIMIT/OFFSET 截取。OFFSET 跳过的行仍需排序。深分页时 OFFSET 大,排序与跳过开销高。

深分页建议 keyset 分页替代 OFFSET。

#
★★

39. MySQL 的 filesort 优化?

说明 MySQL 的 filesort?

  • filesort
  • 排序方式
  • 优化

filesort 是 MySQL 的排序操作(非索引排序时),可能用内存排序或磁盘临时文件。可通过覆盖索引、减少排序列、增大 sort_buffer_size 优化。EXPLAIN 的 Extra 显示 filesort。

filesort 是排序代价的标志,用索引避免。

#
★★

40. ORDER BY 与覆盖索引的取舍?

说明 ORDER BY 与覆盖索引的取舍?

  • 覆盖索引
  • 排序
  • 取舍

ORDER BY 列在索引中可走索引排序避免显式排序;覆盖索引同时避免回表。但索引增加写开销与存储。取舍:高频排序查询用覆盖索引,低频用显式排序。

覆盖索引把排序列与查询列都放进索引,既能走索引排序又能免回表,但代价是写放大与存储增长;取舍标准是查询频率:高频排序查询值得建,低频则让优化器走显式排序,答题抓住以写换读即可。

#
★★

41. MySQL InnoDB 游标的实现,是否物化?与 JDBC ResultSet 的交互?

说明 MySQL InnoDB 游标的实现?

  • 游标物化
  • JDBC ResultSet
  • 流式

MySQL 存储过程游标默认物化(把结果集缓存到内存/临时表),客户端 JDBC ResultSet 默认一次性取结果。用 statement.setFetchSize + 流式读取可逐行拉取。游标物化开销大。

MySQL 存储过程游标默认把结果集物化到内存或临时表,大结果集会耗尽内存;JDBC 客户端默认一次性取回全部结果,setFetchSize 加流式读取才能逐行拉取,理解物化与流式取舍是关键。

#
★★

42. PostgreSQL 中 PL/pgSQL 游标与 SQL 游标的差异?

说明 PL/pgSQL 游标与 SQL 游标的差异?

  • PL/pgSQL 游标
  • SQL 游标
  • 差异

PostgreSQL SQL 游标用 DECLARE CURSOR + FETCH,供客户端使用;PL/pgSQL 游标(FOR 循环、REF CURSOR)在函数内使用。PL/pgSQL 游标更贴近函数逻辑,SQL 游标面向客户端。

用途不同:客户端流式读取 vs 函数内遍历。

#
★★

43. 显式游标与隐式游标的差异,SELECT 自动返回的游标?

说明显式游标与隐式游标的差异?

  • 显式游标
  • 隐式游标
  • 自动返回

显式游标由用户 DECLARE 并显式 FETCH,可控制;隐式游标由数据库自动管理(如 PL/SQL 的 FOR 循环、单条 SELECT 自动打开的游标)。隐式游标更简单但控制弱。

隐式游标自动打开关闭,显式游标可精细控制。

#
★★

44. 服务端游标(Server-side Cursor)与客户端游标(Client-side Cursor)的资源占用差异?

说明服务端与客户端游标的资源占用差异?

  • 服务端游标
  • 客户端游标
  • 资源

服务端游标在数据库端维护结果集状态,占用服务端内存/连接资源;客户端游标把结果拉到客户端,占用客户端内存。服务端游标适合大结果集流式读取,客户端游标简单但内存占用大。

资源占用的位置是两者的本质区别:服务端游标占服务端内存与连接资源,适合大结果集流式处理;客户端游标把结果整体拉到客户端,简单但内存压力大,选型取决于结果集大小与内存预算。

#
★★

45. 流式结果(Streaming Result)协议,MySQL 的 unbuffered result、PostgreSQL 的游标分批读取?

说明流式结果协议:MySQL 的 unbuffered 查询与 PostgreSQL 的游标分批如何减少内存占用?

  • unbuffered result
  • 游标分批
  • 流式

MySQL 的 unbuffered result(unbuffered 查询)逐行拉取,不缓存全部结果;PostgreSQL 用游标分批(FETCH)读取大结果集。流式结果减少内存占用。

流式结果(逐行拉取或分批 FETCH)避免一次性载入大结果集,是处理大导出的关键;答题要说出 MySQL unbuffered 与 PostgreSQL 游标分批两种实现,强调以交互开销换内存。

#
★★

46. 游标的可滚动性(Scrollable),FORWARD ONLY、SCROLL?

说明游标的可滚动性:FORWARD ONLY 与 SCROLL 游标有何区别,各自开销如何?

  • FORWARD ONLY
  • SCROLL
  • 滚动

FORWARD ONLY 游标只能向前读取;SCROLL 游标可前后移动(FETCH PRIOR/RELATIVE 等)。可滚动游标功能强但开销大。默认多为 FORWARD ONLY。

FORWARD ONLY 只能向前读,SCROLL 可前后移动但需维护额外位置状态,开销更大;多数场景只需顺序读取,默认 FORWARD ONLY 足够,答题点出功能增强伴随成本增加的通用规律。

#
★★

47. 游标的敏感性(Sensitivity),INSENSITIVE、SENSITIVE、ASENSITIVE?

说明游标的敏感性:INSENSITIVE、SENSITIVE、ASENSITIVE 三种类型各自如何感知数据变化?

  • INSENSITIVE
  • SENSITIVE
  • ASENSITIVE

游标敏感性表示游标是否感知底层数据变化:INSENSITIVE(不敏感,基于快照,其他会话修改不影响);SENSITIVE(敏感,反映实时变化);ASENSITIVE(由实现决定)。缺省实现而定。

敏感性决定游标看到的数据一致性:INSENSITIVE 基于快照不受其他会话影响,SENSITIVE 实时反映变化,ASENSITIVE 由实现决定;结合事务隔离理解,说明快照更稳定但可能读到旧数据。

#
★★

48. 游标(Cursor)的本质,服务端状态化结果集迭代器?

游标(Cursor)的本质是什么?它如何在服务端维护状态,从而支持大结果集的分批读取?

  • 状态化
  • 迭代器
  • 结果集

游标本质是服务端维护状态的结果集迭代器,记录当前位置,支持 FETCH 逐行读取。它把结果集开放给客户端,实现大结果集分批访问。

游标本质是服务端状态化的结果集迭代器,随连接存活并记录当前位置;理解这一本质才能解释为何游标占用连接资源、为何适合分批读取大结果集,以及为何用完必须关闭以释放资源。

#
★★

49. 游标的 HOLD 特性,事务提交后游标是否仍可用?

说明游标的 HOLD 特性?

  • WITH HOLD
  • 提交后可用
  • 默认

默认游标在事务结束时关闭(创建时的事务)。WITH HOLD 游标在事务 COMMIT 后仍可用(仅 COMMIT 后保留,ROLLBACK 后关闭)。用于事务外继续读取。

WITH HOLD 使游标在 COMMIT 后仍可用,跨事务读取。

DECLARE c CURSOR WITH HOLD FOR SELECT * FROM t;
#
★★

50. DECLARE c CURSOR FOR SELECT ... 的语法?

说明 DECLARE CURSOR 语法?

  • DECLARE
  • CURSOR FOR
  • 语法

DECLARE c CURSOR FOR SELECT ... 声明游标,随后用 OPEN 打开、FETCH 取行、CLOSE 关闭。语法格式因数据库而异。

声明游标后需 OPEN/FETCH/CLOSE 配合使用。

DECLARE c CURSOR FOR SELECT id, name FROM t;
OPEN c;
FETCH NEXT FROM c;
CLOSE c;
#
★★

51. JDBC 中 fetchSize 参数与流式结果?

说明 JDBC fetchSize 与流式结果?

  • fetchSize
  • 流式
  • 内存

JDBC 的 setFetchSize(n) 控制每次网络拉取的行数,配合流式读取(MySQL 需 setFetchSize(Integer.MIN_VALUE) 或 useCursorFetch)实现分批拉取,避免一次加载全部结果。

fetchSize 平衡网络往返与内存占用。

#
★★

52. MySQL 中游标的限制(部分存储引擎)?

说明 MySQL 游标的限制?

  • 存储引擎
  • 游标限制
  • 只读

MySQL 存储过程游标是只读、不可滚动的(只能 FORWARD),且只能在存储过程/函数/事件中使用。MySQL 的游标不支持 UPDATE/DELETE WHERE CURRENT OF。MyISAM 与 InnoDB 在游标使用上无实质差异。

MySQL 存储过程游标只读、单向、不可滚动,且只能用于存储过程、函数或事件,能力远弱于 Oracle 与 SQL Server;答题以受限为主线,补充不支持 WHERE CURRENT OF 即可。

#
★★

53. OFFSET 大分页与游标的取舍?

说明 OFFSET 大分页与游标的取舍?

  • OFFSET 深分页
  • 游标
  • 取舍

OFFSET 大分页会扫描并丢弃大量行,性能差;游标/keyset 分页可基于游标位置持续读取,无需跳过大偏移。对大数据量流式读取,游标优于 OFFSET。

游标维护位置,避免 OFFSET 重复扫描。

#
★★

54. PostgreSQL 中游标的 WITH HOLD 选项?

说明 PostgreSQL 游标的 WITH HOLD?

  • WITH HOLD
  • 事务外
  • 语法

PostgreSQL 的 DECLARE c CURSOR WITH HOLD FOR ... 使游标在事务提交后仍可用,便于跨事务读取。但 WITH HOLD 游标会物化结果(快照),增加内存/磁盘。

WITH HOLD 牺牲实时性换取跨事务可用。

#
★★

55. SQL Server 的 keyset-driven 与 static 游标?

说明 SQL Server 的 keyset 与 static 游标?

  • keyset-driven
  • static
  • 差异

static 游标基于快照,不反映后续数据变化(占用临时表);keyset-driven 游标记录键集,能反映键内非键列的变化,但对删除/新增的处理不同。keyset 介于 static 与 dynamic 之间。

static 游标基于快照,keyset 记录键集并能反映键内数据变化,二者介于 static 与 dynamic 之间形成可见性阶梯;答题要点是快照与键集的差异,决定游标看到的数据新旧。

#
★★

56. cursor_tuple_fraction(PostgreSQL)参数?

说明 cursor_tuple_fraction 参数?

  • 参数
  • 游标估算
  • 优化

cursor_tuple_fraction 是 PostgreSQL 优化器参数,表示游标查询中优化器预期读取的结果比例(默认 0.1)。影响优化器对游标查询的排序/索引选择(小值偏向索引排序,因为假设只读少量行)。

该参数告诉优化器游标查询预计读取的结果比例(默认 0.1),比例小则偏向走索引按序取少量行而非全量排序;理解它就能解释为何同一条游标查询与普通查询可能生成不同的执行计划。

#
★★

57. Oracle 的 LATERAL 实现细节?

说明 Oracle 的 LATERAL 实现?

  • LATERAL
  • Oracle 12c
  • 细节

Oracle 12c+ 支持 LATERAL 子查询与 CROSS/OUTER APPLY。Oracle 的 LATERAL 同样允许引用外层列,执行上相关子查询逐行计算。细节上 Oracle 对 LATERAL 的优化与物化情况与 PostgreSQL 略有差异。

Oracle 用 CROSS APPLY 等价实现 LATERAL。

#
★★

58. DISTINCT 的去重算法,Hash Aggregate、Sort-based Unique?

说明 DISTINCT 的去重算法?

  • Hash Aggregate
  • Sort-based Unique
  • 算法

DISTINCT 去重算法:Hash Aggregate(哈希去重,内存大时快)或 Sort-based Unique(排序后去重,相邻相同合并)。优化器按数据与内存选择。

DISTINCT 可用哈希去重或排序去重:哈希快但耗内存,排序稳定但需排序,优化器按数据量与内存选择;点出两种算法适用条件,并与 GROUP BY 的 Hash/Sort Aggregate 印证。

#
★★

59. 多列排序的字典序,ORDER BY a, b 的二级排序?

说明多列排序 ORDER BY a, b 的字典序语义:先按哪列排序,同值时如何处理,能否各自指定升降序?

  • 字典序
  • 二级排序
  • ORDER BY

ORDER BY a, b 先按 a 排序,a 相同时按 b 排序(字典序/词典序)。可各自指定 ASC/DESC。这是多列排序的标准语义。

多列排序按字典序,先按第一列再按第二列,可各自定序。

SELECT * FROM t ORDER BY a ASC, b DESC;
#
★★

60. 稳定排序(Stable Sort)与不稳定排序的差异,相同键值的顺序保证?

说明稳定与不稳定排序的差异?

  • 稳定排序
  • 相同键值
  • 顺序保证

稳定排序保证相同键值的行保持原输入顺序;不稳定排序不保证。SQL 中 ORDER BY 若不以主键/唯一键定序,相同排序值行的返回顺序不保证稳定。要保证稳定需加唯一键作为末级排序。

SQL 的排序稳定性只在相同键值之间体现:不指定唯一键作末级排序时,相同值行的顺序不保证稳定,分页与去重结果可能漂移;答题要点是要稳定必须在 ORDER BY 末尾加唯一键这一工程结论。

#

61. ORDER BY 与 DISTINCT 的执行顺序?

说明 ORDER BY 与 DISTINCT 的执行顺序?

  • 先 DISTINCT 后 ORDER
  • 排序列限制
  • 逻辑

逻辑上先做 DISTINCT 去重,再对结果 ORDER BY。但 ORDER BY 的列必须出现在 SELECT 列表中(否则语义不明)。执行上可能先排序再 Unique 去重。

ORDER BY 列需在 SELECT 中,与 DISTINCT 协同。

#

62. FETCH NEXT FROM c INTO var 的用法?

说明 FETCH NEXT FROM c INTO var?

  • FETCH
  • INTO 变量
  • 用法

FETCH NEXT FROM cursor INTO var1, var2 从游标取下一行并赋值给变量。可用于逐行处理。无更多行时返回 NOT FOUND。

FETCH NEXT INTO 取下一行赋给变量,用于逐行处理。

FETCH NEXT FROM c INTO v_id, v_name;
#

63. PL/pgSQL 中 REF CURSOR 与变量绑定?

说明 PL/pgSQL 的 REF CURSOR?

  • REF CURSOR
  • 变量绑定
  • 返回游标

REF CURSOR 是游标引用,可在函数中 OPEN 动态查询并返回给调用方,实现动态结果集。PL/pgSQL 中 REF CURSOR 变量绑定查询并返回。

REF CURSOR 用于返回动态游标。

#

64. EXISTS 与 IN 在 SQL 中如何转换为半连接/反连接?优化器的自动转换规则?

说明 EXISTS 与 IN 的自动转换?

  • 半连接
  • 反连接
  • 自动转换

优化器把 EXISTS/IN 子查询自动转换为半连接(Semi Join),把 NOT EXISTS/NOT IN 转换为反连接(Anti Join),用哈希/归并实现。NULL 处理上需注意(IN 含 NULL 时语义特殊)。

优化器自动把 EXISTS/IN 改写为半连接、把 NOT EXISTS/NOT IN 改写为反连接,用哈希或归并实现,这是子查询性能的关键;注意 IN 遇 NULL 语义特殊,是答题补充的边界。

#

65. DISTINCT + 半连接的性能?

说明 DISTINCT + 半连接的性能?

  • 半连接
  • DISTINCT
  • 性能

半连接本身已去重(每行一次),因此无需再 DISTINCT。若用 INNER JOIN 需 DISTINCT 去重,半连接可避免。DISTINCT + 半连接通常应半连接已去重。

半连接天然去重,避免额外 DISTINCT。

#

66. EXISTS 与 COUNT 的差异?

说明 EXISTS 与 COUNT 的差异?

  • EXISTS 存在性
  • COUNT 计数
  • 性能

EXISTS 只判断是否存在(找到即停,用 LIMIT 1 语义),COUNT 统计所有匹配行数。判断存在性时 EXISTS 比 COUNT(*) > 0 更高效(不需全count)。

存在性判断用 EXISTS 优于 COUNT。

#

67. 半连接的代价估算(Hash Semi 的构建代价)?

说明半连接(Hash Semi Join)的代价估算:构建哈希表的代价如何计入,统计信息为何重要?

  • 代价估算
  • 构建哈希
  • 优化器

Hash Semi Join 的代价包括构建右表哈希表(扫描+哈希)与左表探测。优化器按行数、统计信息估算,选择代价最小的算法。构建代价是估算的一部分。

Hash Semi Join 的代价等于构建右表哈希表加探测左表,估算依赖行数与统计信息的准确性;统计信息过旧会导致算法选错,理解代价构成才能在统计失真时预判并修正执行计划。

#

68. 排序的代价模型(磁盘 I/O)?

说明排序的代价模型:内存排序与磁盘外部归并各自的代价构成,为什么磁盘 I/O 是主要开销?

  • 磁盘 I/O
  • 归并
  • 代价

排序代价包括内存排序、磁盘读写(外部归并的多次传递)、临时文件。数据超内存时磁盘 I/O 是主要代价。增加 work_mem 可减少磁盘归并。

排序代价受内存大小与磁盘 I/O 影响。

#

69. CLOSE c 的资源释放?

说明 CLOSE 游标的资源释放?

  • CLOSE
  • 释放资源
  • 游标

CLOSE 关闭游标并释放其关联的资源(服务端结果集、临时表、锁)。应显式 CLOSE 避免资源泄漏。不关闭的游标在事务结束/连接断开时释放。

显式 CLOSE 是良好实践,释放服务端资源。

#

70. 游标与 FOR 循环的对比?

说明游标与 FOR 循环的对比?

  • FOR 循环
  • 游标
  • 隐式

FOR 循环(如 PL/pgSQL 的 FOR rec IN SELECT 或 PL/SQL)隐式维护游标,逐行处理,代码简洁;显式游标需手动 OPEN/FETCH/CLOSE,控制精细。FOR 循环通常更易读。

FOR 循环是显式游标的隐式封装:自动 OPEN/FETCH/CLOSE,代码简洁但控制力弱;答题对比隐式省事与显式可控,并说明 PL/pgSQL 中 FOR 循环逐行处理同样基于游标机制。

#

71. 游标的性能,逐行处理 vs 集合处理?

说明游标逐行处理与集合处理的性能?

  • 逐行处理慢
  • 集合处理快
  • 建议

游标逐行处理(ROW-BY-ROW)有函数调用与上下文切换开销,性能差;集合处理(SET-BASED)一次处理一批,性能好。应尽量用集合操作,避免逐行游标处理大数据。

集合处理是 SQL 最佳实践,游标逐行是反模式。

#

72. 游标的错误处理(NOT FOUND)?

说明游标的错误处理:取不到行时触发 NOT FOUND(NO_DATA_FOUND)如何用于循环结束?

  • NOT FOUND
  • 异常
  • 结束

游标取不到更多行时触发 NOT FOUND 条件(SQL 中 SQLSTATE 02000,PL/SQL 中 NO_DATA_FOUND),用于循环结束判断。需正确处理。

NOT FOUND 是游标循环终止的机制。