重点:不报语法异常,SQL 正常执行,但结果数据错误,线上难排查,不是语法报错,是逻辑结果跑偏。 适合 ERP、财务系统迁移场景。
1. substr /substring 下标(你遇到的)
表格
| 项 | Oracle | MySQL | 现象 |
|---|---|---|---|
| 起始位 0 | substr('ABC',0,3) → ABC | substr('ABC',0,3) → '' | MySQL 直接返回空,不报错 |
| 正数偏移 | 从 1 开始,0 等价 1 | 严格从 1 开始,0 代表第 0 位无字符 | 静默返回空,业务截取错误 |
| 负数 | substr('ABC',-2,2) → BC | substr('ABC',-2,2) → BC | 负数行为一致,无坑 |
✅兼容写法:统一用 start=1,不要写 0;统一 case when,不要依赖 oracle if 函数。
2. 空字符串 '' 与 NULL(超级高频静默坑)
Oracle 中
'' = NULL;MySQL 中'' != NULL,完全两回事!
Oracle
sql
select case when '' is null then 1 else 0 end from dual; -- 返回1
select '' is null from dual; -- 等价null
Oracle:空字符串会被当成 null。
MySQL
sql
select case when '' is null then 1 else 0 end ; -- 返回0
MySQL:''是空字符串,null是 null,二者不等。
典型静默故障场景
- oracle 迁移过来,
where col <> '',oracle 查不出 null,mysql 可以查出 '',过滤条件失效; nvl(col,'')迁移到 mysql 写ifnull(col,'')- Oracle:如果 col 为 null,输出 '';但 oracle 里 '' 又等于 null;
- MySQL:ifnull (col,'') 会把 null 转为真实空字符串,后续
is null判断直接失效。
- 联合唯一索引:oracle 多列唯一索引允许多个 null;mysql 允许多个
null,但不允许多个 '' 空字符串,会报唯一键冲突。
财务系统:合同号、发票号字段迁移极易踩这个。
✅迁移建议:
- 尽量统一使用
null,业务少用字面量''; - 不要直接把 oracle nvl (col,'') 无脑转 ifnull (col,'')。
3. 日期隐式转换(静默丢数据 / 查不出数据)
Oracle
date可以直接和字符串比较 where create_time = '2026-09-02',自动隐式转换。
MySQL
- datetime 字段和字符串格式不对,不会报错,匹配不到数据;
- Oracle
sysdate返回带时分秒日期;MySQLsysdate()/now(); - Oracle
trunc(sysdate)截取到日期(去掉时分秒) MySQL:date(sysdate())
⚠️大坑:Oracle to_date('20260902','yyyymmdd');MySQL str_to_date('20260902','%Y%m%d') 写错格式符,不会抛错,返回 null,查询结果为空。
表格
| 功能 | Oracle | MySQL |
|---|---|---|
| 当前时间 | sysdate | now() / sysdate() |
| 截断到天 | trunc(sysdate) | date(now()) |
| 字符串转日期 | to_date(col,'yyyymmdd') | str_to_date(col,'%Y%m%d') |
现象:程序不报错,查询返回 0 行,业务查不到单据。
4. 数字隐式转换:字符串字段跟数字比较(经典坑)
Oracle
sql
-- invoice_num varchar2,值'00123'
where invoice_num = 123; -- oracle 两边都转数字,匹配成功
MySQL
sql
where invoice_num = 123;
MySQL 字符串列和数字对比,会把全表字符串转数字:
'00123' →123;'CMS123' →0;所有以非数字开头字符串全部转为 0。 现象:大量错误匹配,查出完全无关数据,无报错!
✅强制两边都用字符串比较:where invoice_num='123',禁止 varchar 列直接跟数字常量做等于、in。
RCSS/ERP 单据号大多 varchar,这个坑破坏力极强。
5. order by 别名行为差异
Oracle:order by 别名支持;MySQL 默认支持,但特殊模式下不支持;
重点坑:group by + order by 列别名,配合 only_full_group_by
Oracle:group by 非聚合字段,oracle 允许; MySQL 开启only_full_group_by(mysql5.7 + 默认开启)直接报错;
但如果关闭这个参数,MySQL不会报错,随机返回分组内任意一行数据!!【超级危险静默错误】
sql
-- 关闭only_full_group_by下,mysql静默乱取数据,无报错
select source, max(amount) from t_invoice group by source;
-- 若select带上非分组、非聚合字段,mysql返回随机行,业务统计报表错乱。
财务报表统计场景,这个坑会直接造成金额统计错误。
✅迁移:严格遵循分组语义,select 字段要么 group by,要么聚合函数。
6. 空值排序 null 排序顺序
sql
order by col asc
- Oracle:
null排在最后面 - MySQL:
null排在最前面
现象:分页列表、单据列表,null 字段的行,迁移后跑到列表头部,业务排序错乱,无报错。
示例:按核销时间排序,核销时间为 null 未核销的单据,oracle 全部在末尾,mysql 跑到第一页。
✅兼容写法:
sql
-- mysql模拟oracle null放末尾
order by col is null, col asc;
7. length /char_length 字符字节坑
Oracle length():按字符计算; MySQL
length():按字节(utf8 中文占 3 字节)char_length():按字符
sql
-- 中文“测试”
oracle length('测试') →2
mysql length('测试') →6
mysql char_length('测试') →2
现象:判断字段字符长度校验逻辑迁移后,校验条件失效,超长校验逻辑错乱。 ✅mysql 只要判断字符数量,统一使用
char_length()。
8. decode 转 if /case 容易漏掉 else
Oracle decode:
sql
decode(source,'CMS','1','ERP','2',null)
decode 没有匹配返回 null; 迁移 mysql 写 if 或者 case when,如果手写漏写 else,mysql 也是返回 null,这点行为一致; ⚠️但很多人迁移顺手写 else '',把 null 变成空字符串,引入第 2 点的 '' 和 null 静默坑。
9. rownum vs limit
注意:这个容易写语法错误,但写错也会出现静默结果异常 Oracle 分页:
where rownum <=10; MySQL 分页:limit offset,size; ⚠️重点:oracle rownum 不能直接where rownum>10,查不到数据;mysql limit offset=10 可以正常跳过。
10. 多表 in 子查询 NULL 坑
sql
where id in (select col from b)
子查询返回任意一个 null:
- Oracle:整个 in 表达式返回 null,查询无结果;
- MySQL:正常执行过滤。 业务迁移,迁移前后查询结果行数不一致,无报错。
📋 静默错误坑速记清单(开发自测检查点)
- ✅substr 起始下标:oracle 0 等价 1;mysql 0 返回空,禁止写 0
- ✅'' 空字符串 vs null:oracle 二者等价;mysql 完全不等,迁移 nvl 不要无脑转 ifnull (col,'')
- ✅varchar 字段不要直接和数字常量比较,防止 mysql 隐式转换乱匹配
- ✅分组查询,确认 mysql 开启 only_full_group_by,防止随机行返回
- ✅null 排序:oracle 放末尾,mysql 放开头,分页列表要兼容处理
- ✅长度函数:mysql 中文统计字符用 char_length,不要用 length
- ✅日期函数:trunc→date (),to_date→str_to_date,格式符不要抄 oracle
- ✅in 子查询包含 null,两边返回结果行为不一致
全部都是 SQL 正常执行,无报错,业务数据错误,单元测试非常容易漏掉
341



被折叠的 条评论
为什么被折叠?



