
目录前言一、现场现象迁移后 LEFT JOIN 查询诡异丢数据1. 实际业务场景2. 沿用源库的错误 SQL 写法业务预期结果KES 实际运行结果执行计划真实佐证二、底层原理为什么 KES 会改写 LEFT JOIN 为内连接2.1 SQL 执行顺序导致的隐性过滤2.2 KES 优化器的性能优化逻辑三、特殊豁免场景不会触发外连接消除的情况安全无坑示例 SQL核心内核逻辑四、KES 迁移实战避坑方案方案一右表过滤条件全部下移至 ON 子句通用最优解方案二Oracle() 兼容语法专属处理方式方案三左表 WHERE 过滤无任何丢数据风险方案四通过执行计划快速排查隐患 SQL五、迁移落地总结与团队开发规范1. 核心开发准则2. 迁移落地优先级建议前言最近在负责多套 MySQL、PostgreSQL 业务系统迁移金仓 KES 的落地工作过程中碰到了一个非常隐蔽且折磨人的问题很多原本在源库运行正常的LEFT JOIN查询迁移到 KES 后业务数据莫名少了一大半。最让人头疼的是程序完全没有报错SQL 语句一行没改、业务逻辑也完全没变单纯换了数据库结果集就对不上了。初期排查时我们反复核对代码、校验表数据、比对业务逻辑始终找不到问题根源一度怀疑是数据库迁移同步数据出了问题。后续拆解执行计划才发现这根本不是数据同步问题也不是 KES 数据库 Bug而是 KES 优化器的外连接消除机制在悄悄生效。相比于 MySQL、PostgreSQLKES 的查询优化更加激进会自动改写外连接语句将 LEFT JOIN 转为 INNER JOIN最终导致业务数据丢失。这是异构数据库迁移中非常典型的兼容性坑大量开发、运维、DBA 都会中招。我结合本次落地踩坑经验从现场现象、底层原因、特殊场景、实操解决方案全方位梳理给正在做 KES 迁移的小伙伴避坑。一、现场现象迁移后 LEFT JOIN 查询诡异丢数据1. 实际业务场景为了方便大家复现和理解我简化了线上业务模型核心就是两张最普通的关联表也是日常开发中最常用的一对多关联场景t1主业务表左表存储核心业务数据业务需求要求必须保留全量数据无论关联附表是否有数据主表记录都不能丢失。t2附属信息表右表存储主表的拓展数据属于可选关联无匹配数据时字段展示 NULL 即可。本次业务需求很简单查询 t1 所有数据仅关联匹配 t2 中 name2 等于「cc」的记录不匹配的 t1 数据正常保留附表字段置空。这套逻辑在 MySQL、PostgreSQL 中运行完全正常结果符合业务预期但迁移至 KES 后直接出现数据缺失。2. 沿用源库的错误 SQL 写法很多人迁移时会直接复用原有 SQL这也是踩坑的核心原因下面是典型的错误写法-- 危险写法将右表过滤条件写在 WHERE 子句中 SELECT * FROM t1 LEFT JOIN t2 ON t1.id1 t2.id2 WHERE t2.name2 cc;业务预期结果完整返回 t1 全部数据行t2 匹配成功且 name2 为 cc 的展示对应附表数据未匹配的 t1 数据保留t2 相关字段显示 NULL。KES 实际运行结果只返回 t1 和 t2 完全匹配、且满足 name2cc 的交集数据所有无附表匹配的主表数据全部被过滤掉相当于彻底丢失了一部分业务数据。执行计划真实佐证我们通过 KES 执行计划排查后发现语句中明明写的是 LEFT JOIN但执行计划里已经没有了左外连接算子被自动替换成了普通的 Hash Join、Nested Loop 内连接执行逻辑。这就是 KES 优化器自动触发的外连接消除也是数据丢失的真正原因。二、底层原理为什么 KES 会改写 LEFT JOIN 为内连接很多开发同学疑惑明明写了左连接保数据数据库为什么会自作主张改写法其实这是由 SQL 执行规则和 KES 优化器的智能等价变换共同决定的并非数据库异常。2.1 SQL 执行顺序导致的隐性过滤标准 SQL 的执行逻辑是先关联 JOIN再执行 WHERE 过滤这个顺序是所有数据库通用的也是问题的根源第一步执行 LEFT JOIN 关联后所有 t1 数据都会保留没有匹配 t2 的数据行t2 所有字段都会填充为 NULL 第二步执行 WHERE t2.name2 cc 过滤条件数据库判断 NULL cc 属于未知不匹配结果会直接过滤掉这部分空值行 最终结果就是所有不匹配的主表数据全部被剔除和直接使用 INNER JOIN 的查询结果完全一样。2.2 KES 优化器的性能优化逻辑相比于 MySQL、PG 的宽松优化策略KES 优化器的等价校验更加严格。优化器在解析 SQL 时会自动判断语句的逻辑等价性如果「LEFT JOIN WHERE 右表常规过滤」的最终结果和 INNER JOIN 完全一致那外连接就没有任何存在意义。为了降低查询开销、减少数据扫描和关联计算成本KES 会主动做优化改写直接将外连接降级为效率更高的内连接。站在数据库性能角度这个优化是合理的但对于迁移业务来说就会直接打破原有业务逻辑造成数据丢失。三、特殊豁免场景不会触发外连接消除的情况这里重点提醒大家不是所有写在 WHERE 里的右表条件都会触发优化改写有且仅有一种特殊场景是豁免的也是我们日常查询空数据常用的写法。如果 WHERE 条件是右表 IS NULL 判空KES 绝对不会执行外连接消除。安全无坑示例 SQL-- 安全写法查询主表中无附表匹配的数据不会触发外连接消除 SELECT * FROM t1 LEFT JOIN t2 ON t1.id1 t2.id2 WHERE t2.name2 IS NULL;核心内核逻辑IS NULL 的语义本身就是为了筛选 LEFT JOIN 产生的空值数据。如果优化器把这条语句改成内连接就彻底查不到「主表无关联数据」的记录直接导致业务功能失效。KES 优化器能够精准识别这种业务语义为了保证查询结果绝对准确会主动跳过优化改写逻辑完整保留 LEFT JOIN 特性。这也是我们排查问题、编写空值查询时需要牢记的关键点。四、KES 迁移实战避坑方案结合本次迁移踩坑经验我整理了四套高频实用的解决方案覆盖标准 SQL 写法、Oracle 兼容语法、条件区分、问题排查适配绝大多数迁移场景。方案一右表过滤条件全部下移至 ON 子句通用最优解这是最稳妥、兼容性最好、全数据库通用的修复方式也是官方推荐的开发规范。大家只要记住一个简单的判断逻辑想要保留左表全量数据所有针对右表的筛选条件一律写在 ON 后面不要写在 WHERE 后面。调整后执行逻辑会完全改变数据库先过滤右表有效数据再和左表做外连接从根源上避免空值行被过滤彻底杜绝外连接消除问题。-- 修复后标准写法KES、MySQL、PG 全兼容无数据丢失问题 SELECT * FROM t1 LEFT JOIN t2 ON t1.id1 t2.id2 AND t2.name2 cc; -- 附表筛选条件放入ON子句方案二Oracle() 兼容语法专属处理方式很多老旧业务是从 Oracle 迁移至 KES会沿用 KES 兼容的 Oracle() 隐式外连接语法这也是高频踩坑点很多人因为写法不规范导致隐性丢数据。核心规则非常关键使用()语法时WHERE 子句中所有右表的过滤条件必须全部带上()否则一定会触发外连接消除。错误示范丢数据SELECT * FROM t1, t2 WHERE t1.id1 t2.id2() AND t2.name2 cc; -- 右表条件未加()触发优化改写数据丢失正确示范无风险SELECT * FROM t1, t2 WHERE t1.id1 t2.id2() AND t2.name2() cc; -- 右表条件统一加()等价ON子句过滤补充说明()语法仅适配 Oracle 兼容模式新项目建议直接使用标准 LEFT JOIN 写法可读性和兼容性更好。方案三左表 WHERE 过滤无任何丢数据风险很多同学容易混淆左右表条件的区别这里明确说明只有右表的常规 WHERE 过滤会触发外连接消除针对左主表的 WHERE 过滤是绝对安全的。左表是外连接的驱动表对左表的过滤属于业务前置筛选只会剔除不符合条件的主表数据不会改变 LEFT JOIN 的外连接属性KES 不会做任何优化改写。-- 左表过滤安全写法无外连接消除风险 SELECT * FROM t1 LEFT JOIN t2 ON t1.id1 t2.id2 WHERE t1.name1 a;方案四通过执行计划快速排查隐患 SQL在批量迁移上线阶段不可能逐条人工校验逻辑最高效的方式就是通过 KES 执行计划批量筛查问题 SQL。给大家整理了快速判断标准正常 LEFT JOIN执行计划包含 Left Join / Left Outer 算子异常被优化改写算子变为 Hash Join / Nested Loop无 Left 标识同时结果集偏少日常排查可直接使用以下命令快速定位异常语句EXPLAIN ANALYZE SELECT * FROM t1 LEFT JOIN t2 ON t1.id1 t2.id2 WHERE t2.name2 cc;五、迁移落地总结与团队开发规范经过本次批量迁移踩坑我们团队统一整理了适配 MySQL、PG 迁移 KES 的开发规范规避后续批量出现同类问题非常适合团队落地执行。1. 核心开发准则分清语义边界ON 子句只用来定义表与表的关联规则WHERE 子句只用来做最终结果的全局过滤规范附表过滤除 IS NULL 空值查询外所有右表筛选条件统一迁移至 ON 子句老旧语法规范使用 Oracle() 语法时右表所有过滤条件必须统一追加()符号。2. 迁移落地优先级建议异构数据库迁移数据一致性永远优先于性能优化。KES 的外连接消除是优秀的性能优化机制但在迁移初期我们必须优先对齐 MySQL、PG 的原有业务逻辑保证数据准确无误后续再根据业务场景做性能调优。同时建议将 JOIN 类 SQL 纳入上线强制审计范围只要是含 LEFT/RIGHT JOIN 的语句必须查看执行计划确认连接类型未被自动改写。批量迁移场景下可通过脚本批量扫描 WHERE 子句中的右表过滤条件统一批量整改提升迁移效率。