核心坑点:Oracle substr 起始下标从 0 开始;MySQL substr/substring 起始下标从 1 开始,这是最容易踩的隐形 BUG。
你的 SQL 对照
Oracle
sql
sql
if(i.source='CMS',substr(i.invoice_num,0,3),'') as reverse1
Oracle substr(str, start, len)
- start=0:等价于从第1 位开始截取,取 3 个字符。
MySQL 错误写法(直接照搬 0 会出错)
sql
sql
-- ❌错误:mysql substr start=0 返回空字符串!
if(i.source='CMS',substr(i.invoice_num,0,3),'') as reverse1
MySQL 正确写法
sql
sql
-- ✅正确:mysql下标从1开始,取前3位
if(i.source='CMS',substr(i.invoice_num,1,3),'') as reverse1
所以你写的
substr(i.invoice_num,1,3)是对的。
substr 完整对照表
表格
| 场景 | Oracle substr | MySQL substr / substring | 说明 |
|---|---|---|---|
| 截取前 3 位 | substr(col,0,3) |
substr(col,1,3) |
Oracle 0 等价 1;MySQL 传 0 得到空 |
| 从第 2 位开始取 4 位 | substr(col,1,4) |
substr(col,2,4) |
偏移全部差 1 |
| 倒数第 2 位取 2 字符 | substr(col,-2,2) |
substr(col,-2,2) |
负数下标两边行为一致 |
重点:负数索引两者完全一样,只有正数起始位差异巨大。
其他兼容坑(Oracle→MySQL)
- if 函数
- Oracle 没有
if()函数,Oracle 用case when/decode
sql
-- Oracle原生等价改写
case when i.source='CMS' then substr(i.invoice_num,0,3) else '' end as reverse1
- MySQL 支持
if(条件,true值,false值),也支持 case when。
- 空字符串 '' vs null
- Oracle:
'' 等价 null;MySQL:''是空字符串,不等于null,迁移也要小心。
- 长度函数
- Oracle:
length() - MySQL:
length()字节、char_length()字符;中文场景强烈建议用char_length。
兼容小建议
如果要一套 SQL 尽量兼容两边:
不要用 0 作为 substr 起始,Oracle 统一写
substr(col,1,3),效果和substr(col,0,3)完全一样!
✅Oracle 也可以直接写 start=1,效果不变:
sql
-- oracle中 substr(col,0,3) = substr(col,1,3)
substr(i.invoice_num,1,3)
这样 Oracle、MySQL 两边 SQL 写法完全一致,避免下标 0 陷阱。
最终两边统一版本(推荐)
sql
case when i.source='CMS' then substr(i.invoice_num,1,3) else '' end as reverse1
使用 case when,同时兼容 Oracle 与 MySQL,消除 if 函数、下标 0 两个隐患。
踩坑复盘
这个坑属于静默错误:不会抛异常,只是返回空字符串,业务数据错了很难排查,测试很容易漏掉。