子查询不能驱动多级审批流程状态跟踪,因其仅作快照式查找,无法表达“流程进度”语义;正确做法是结合状态字段、审批记录表及分层逻辑,通过校验前序完成、本步启用与权限匹配来判定当前待办节点。

子查询本身不能驱动多级审批流程的状态跟踪,它只是状态查询的辅助工具;真正需要的是设计合理的状态字段、审批记录表和分层查询逻辑。
为什么直接用子查询查“当前审批人”容易出错
常见错误是写类似 SELECT (SELECT user_id FROM approvals WHERE step = (SELECT MAX(step) FROM approvals WHERE doc_id = t.id AND status = 'pending')) FROM docs t —— 这种嵌套在数据不完整(比如某步被跳过、状态未及时更新)时会返回空或错值。
根本问题在于:子查询无法表达“流程进度”的语义,它只做快照式查找。实际审批中,“当前待办”取决于上一步是否完成 + 本步是否启用 + 权限是否匹配,不是单纯取最大 step。
- 审批步骤可能非连续(如 step=1,3,5),
MAX(step)会漏掉中间跳转逻辑 - 同一
doc_id可能存在多条status = 'pending'记录(并发提交冲突) - 子查询不校验时间顺序,
created_at滞后于前序完成时间的记录会被误判为“当前”
用关联子查询查“各环节处理结果”更可靠
把每级审批结果作为独立列查出,比试图算出“当前谁该审”更稳定。适合报表、看板等读多写少场景。
示例结构:approvals(doc_id, step, user_id, status, approved_at),假设最多 3 级:
SELECT d.id, d.title, (SELECT status FROM approvals a1 WHERE a1.doc_id = d.id AND a1.step = 1) AS step1_status, (SELECT user_id FROM approvals a1 WHERE a1.doc_id = d.id AND a1.step = 1) AS step1_user, (SELECT approved_at FROM approvals a1 WHERE a1.doc_id = d.id AND a1.step = 1) AS step1_time, (SELECT status FROM approvals a2 WHERE a2.doc_id = d.id AND a2.step = 2) AS step2_status, (SELECT status FROM approvals a3 WHERE a3.doc_id = d.id AND a3.step = 3) AS step3_status FROM docs d;
这样每列含义清晰,前端可直接判断:step1_status = 'approved' AND step2_status = 'pending' 即表示卡在第二步。
- 避免用
IN或EXISTS替代——它们只返回布尔值,丢失具体状态细节 - 对每 step 使用独立子查询,比
LEFT JOIN更易控制 NULL 行为(尤其当某 step 尚未触发时) - 注意加索引:
approvals(doc_id, step)是必须的,否则 N+1 查询性能崩盘
用相关子查询判断“是否完成全流程”
检查所有必经步骤是否都为 'approved',比查“最后一步状态”更准确——因为流程可能支持条件分支,最后 step 不一定代表终态。
关键逻辑:只有当指定步骤全部存在且状态达标,才算完成。
SELECT id, title,
CASE
WHEN (
SELECT COUNT(*) = 3 FROM (
SELECT 1 FROM approvals a WHERE a.doc_id = d.id AND a.step IN (1,2,3) AND a.status = 'approved'
) t
) THEN 'completed'
ELSE 'in_progress'
END AS flow_status
FROM docs d;
这个写法强制要求 step 1/2/3 全部存在且通过,不会被“step=3 已批但 step=1 被撤回”这类异常绕过。
- 若步骤数动态配置,需改用
SELECT COUNT(DISTINCT step)配合步骤定义表 - 别用
NOT EXISTS (SELECT ... WHERE status != 'approved')——它会把缺失记录也判为 TRUE - MySQL 8.0+ 可考虑用
JSON_CONTAINS预存步骤规则,但子查询方式兼容性更好
真正难的不是怎么写子查询,而是定义清楚“什么是审批完成”。流程引擎里常见的“并行分支”“驳回重走”“超时自动跳过”,这些状态转换规则必须先沉淀到业务逻辑或状态机表中,子查询只是把结果翻出来给你看。没想明白状态流转,再漂亮的 SQL 也是空中楼阁。











