Firebird 2.5如何借助生成器高性能获取Client表最后ID
解决方案
你当前的查询返回最小ID的原因很简单:没有指定排序时,Firebird会按你建的ASC升序索引的存储顺序(从小到大)返回行,FIRST 1拿到的自然是第一条最小的ID。
首先纠正一个错误认知:Firebird 2.5支持对ASC升序索引做反向遍历,ORDER BY id DESC只要命中索引,根本不会全表扫描,性能是常量级。你之前觉得这个写法性能差,大概率是没触发索引范围扫描,不是写法本身的问题。
如果你坚持要通过追加AND条件实现、不使用ORDER BY DESC、也不调用MAX(),直接在原有语句后加不存在更大ID的判断即可,100%命中现有ASC索引,性能和MAX()完全一致:
SELECT FIRST 1 c.id FROM client c WHERE c.id <= GEN_ID(GEN_CLIENT_ID, 0) AND NOT EXISTS (SELECT 1 FROM client c2 WHERE c2.id > c.id)
性能原理
这个写法不会遍历全表:优化器会把NOT EXISTS的判断逻辑直接转换为ASC索引的端点查找,直接定位到索引最右侧的叶子节点(也就是存储最大ID的位置),单次索引寻址就能拿到结果,没有排序开销、没有全表扫描开销。你可以在执行前查看执行计划,正常会显示全程走你现有的ID升序索引,不会出现NATURAL(全表扫描)、SORT(排序)的执行节点。
可选高效写法
如果你不排斥显式指定执行计划,哪怕用ORDER BY DESC也可以强制走索引反向扫描,性能和上面的写法完全相同:
SELECT FIRST 1 c.id FROM client c WHERE c.id <= GEN_ID(GEN_CLIENT_ID, 0) ORDER BY c.id DESC PLAN (c ORDER IDX_CLIENT_ID DESC) -- 把IDX_CLIENT_ID替换成你实际的ID升序索引名即可
这个写法同样是直接从索引尾部开始读第一条匹配值,不会扫全表。
内容的提问来源于stack exchange,提问作者orochimario
相关产品推荐
相关产品推荐

