写报表时要把订单号里的日期段取出来,字段是 20241030-0001,只想留前 8 位。顺手写了 SUBSTR(order_no, 1, 8),结果没问题;换一个从右边取后四位的场景,起始位写成 -4 才反应过来,负数是从末尾往前数的。
三个参数各管什么
oracle substr 的基本写法是 SUBSTR(字符串, 起始位置, 长度)。起始位置从 1 开始,写 0 也按 1 处理;长度省略就一直取到结尾。第三个参数给 0 或者负数,返回空值,不会报错,排查时容易漏看。
SELECT SUBSTR('20241030-0001', 1, 8) FROM dual;SELECT SUBSTR('20241030-0001', -4) FROM dual;SELECT SUBSTR('20241030-0001', 10, 4) FROM dual;中文字符按字算,按字节要用 SUBSTRB
VARCHAR2 里一个汉字算一个字符,SUBSTR 取 3 位就是 3 个汉字。字段是 NVARCHAR2 也一样按字符算。真按字节截取时用 SUBSTRB,一个 UTF-8 汉字通常占 3 个字节,长度给 3 才够一个字,给 2 可能截出半个字,界面显示成问号或乱码。GBK 库一般占 2 个字节,换库前先确认字符集。
和 INSTR 搭配取中间一段
取两个分隔符之间的内容,思路是先定位再截。比如从 abc@example.com 取 @ 前面的用户名:
SELECT SUBSTR('abc@example.com', 1, INSTR('abc@example.com', '@') - 1) FROM dual;INSTR 返回 @ 的位置,减 1 正好到前一位。分隔符不存在时 INSTR 返回 0,长度变成 -1,SUBSTR 返回空,不会把整串吐出来。要取 @ 后面的域名,起始位写 INSTR(...) + 1,长度省略。
几个容易踩的坑
- 起始位超过字符串长度,返回 NULL,不是原串。
- 长度写成小数会先四舍五入再截,别在长度里做除法不取整。
- SUBSTR 在 SQL 和 PL/SQL 里都能用,PL/SQL 里对 CLOB 大字段建议先转成 VARCHAR2 再截。
- 条件里对字段套 SUBSTR 会让索引失效,能改成范围查询就改。
日常取固定位置用 SUBSTR 最省事,格式不固定就老实用 INSTR 配合,别靠数位置硬写。oracle substr 记住两点:起始位从 1 开始,负数从尾部算,八成场景够用。