首页/数据/SQL 速查/标量子查询

标量子查询

用于取值场景的子查询:必须至多返回一行一列;没有匹配行时结果为 NULL。

SQL 速查数据更新于 2026-10-13

标量子查询的取值规则

标量子查询是出现在「需要一个值」位置上的子查询,例如查询列表、比较运算的一侧、更新语句的赋值处。规则有两条:必须至多返回一行一列,返回多行直接报错;返回零行时结果不是报错而是 NULL,它会继续参与外层表达式并可能把整行结果变成 NULL。这两条规则决定了它的适用边界,也是线上最常见的事故点。

基本信息

适用平台MySQL 8.0 / PostgreSQL 12 及以上
常见位置查询列表、比较条件、赋值语句的右侧
行数限制至多一行一列,多行时语句直接失败
空结果行为没有匹配行时返回 NULL,而不是报错
典型报错MySQL 错误 1242;PostgreSQL 提示子查询返回多于一行

典型使用场景

  • 给明细行附一个全局参考值,例如全部订单里的最大金额
  • 取某个对象最近一次操作时间,用于计算间隔天数
  • 更新语句里把子查询结果赋给目标列
  • 配置类取值:取一条系统参数作为默认值参与计算
  • 兼容性改写:把原先的相关子查询换成更易读的取值形式

示例与报错写法

示例与报错写法
-- 正确用法:子查询保证只返回一行一列
SELECT o.id, o.amount,
       (SELECT MAX(amount) FROM orders) AS max_amount
FROM orders o
WHERE o.id <= 10;

-- 没有匹配行时返回 NULL,参与运算会让整行结果变空
SELECT u.id, (SELECT MAX(o.amount) FROM orders o WHERE o.user_id = u.id) AS max_amt
FROM users u;

-- 危险写法:子查询未限制返回行数,多行时直接报错
SELECT u.id, (SELECT o.amount FROM orders o WHERE o.user_id = u.id) AS amt FROM users u;

-- 稳妥写法:显式收敛成一行,并处理空值
SELECT u.id, COALESCE((SELECT MAX(o.amount) FROM orders o WHERE o.user_id = u.id), 0) AS max_amt
FROM users u;

执行行为对照

子查询形态执行方式需要注意
不引用外层的常量式子查询通常只计算一次并复用结果结果与数据快照有关,长事务里可能读到旧值
引用外层列的相关子查询逻辑上逐行求值外层行数大且内表无索引时代价急剧上升
返回多行的子查询直接报错终止要么加聚合收敛,要么改用连接
返回零行的子查询结果为 NULL参与算术或比较后整行可能变为 NULL
把标量子查询当成「保证只有一个值」来用时要真正确认唯一性:常见办法是加聚合函数、加排序加数量限制,或直接在业务键上确认唯一约束。仅凭数据现状「现在只有一行」是不够的,数据增长后语句会在最不该出错的时候失败。
相关标量子查询在内表连接列有索引时能跑得不错;一旦内表缺索引,外层每一行都要扫一遍内表,大表上表现为典型的慢查询,改写方向通常是改成左连接加分组聚合,或用窗口函数一次性算出。

易错点

  • 子查询返回多行时直接报错(MySQL 1242),上线前要用数据量最大的账号做验证,别只在测试库跑
  • 返回空结果得到 NULL,参与算术或比较后整行结果都变成 NULL,需要 COALESCE 兜底
  • 用标量子查询逐行取值等价于嵌套循环:外层行数大、内层无索引时性能断崖式下滑
  • 把标量子查询当连接使用:需要多列或多行时应该改成连接,硬凑会写出难以维护的嵌套

数据来源与参考

  • MySQL 8.0 Reference Manual · Scalar Subqueries
  • PostgreSQL Documentation · Scalar Subqueries
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌