在核对一张业务表的字段长度,用两个函数做了同样的校验,结果不相同:同一个字段,LENGTH(col) 返回 5,LENGTHB(col) 返回 10。顺着这两个函数检查整理,发现它们还与 VARCHAR(n) 的长度口径、字符集、跨库迁移相关,下面是相关介绍。
LENGTH 数字符:一个汉字算 1 个,一个字母也算 1 个。
LENGTHB 数字节:一个汉字占几个字节,由数据库字符集决定。
GB18030(达梦默认字符集)下汉字 2 字节,UTF-8 下汉字 3 字节,字母和数字都是 1 字节。
‘达梦数据库’ 这 5 个字,在 GB18030 实例里 LENGTHB 是 10,换成 UTF-8 就是 15。字符串没变,结果跟着字符集变。
在 GB18030 的实例上实测:
– 建测试表
CREATE TABLE t_len_test (id INT, col1 VARCHAR(50));
– 插入中英文混合数据
INSERT INTO t_len_test VALUES (1, ‘达梦数据库’);
INSERT INTO t_len_test VALUES (2, ‘DM数据库2026’);
INSERT INTO t_len_test VALUES (3, ‘abc’);
– 对比两个函数的结果
SELECT id,
col1,
LENGTH(col1) AS “字符数”,
LENGTHB(col1) AS “字节数”
FROM t_len_test;
结果如下:
‘DM数据库2026’ 这行拆开算:‘DM’ 占 2 字节,‘数据库’ 占 6 字节,‘2026’ 占 4 字节,加起来 12。
纯字母数字的列,两个函数结果永远相同;混进汉字,差距就拉开了。
达梦 VARCHAR(n) 的 n 有两种口径,由初始化参数 LENGTH_IN_CHAR 决定:
LENGTH_IN_CHAR=0(默认):n 按字节。
UTF-8 下 VARCHAR(10) 最多放 3 个汉字(10÷3 取整),GB18030 下最多放 5 个。这种库插入前校验要用 LENGTHB,因为它的返回值直接对应列放不放得下。
LENGTH_IN_CHAR=1:n 按字符。VARCHAR(10) 就是 10 个汉字,校验用 LENGTH。
目前LENGTH_IN_CHAR参数从 DM 8.1.3.162(2024 年二季度)开始被废弃,统一按字节存储;需要按字符定义时,可以在建表语句里写 VARCHAR(50 CHAR)。
老实例(废弃前)和新实例上同样的 DDL 行为可能不一样,迁移之前先查目标库参数,最好实际建表验证一次。
总之,字符集是初始化实例时定下来的,建库之后改不了。如果做过跨字符集的数据迁移,源库和目标库的 LENGTHB 结果会对不上,LENGTH 则不受影响,因此迁移之前先搞清楚两边的字符集。
Oracle 迁达梦:
语法兼容,字符集可能变。Oracle 常用 AL32UTF8(汉字 3 字节),达梦默认 GB18030(汉字 2 字节),同一段数据两边 LENGTHB 结果不同。对账对不上先查字符集:SELECT UNICODE; 返回 0 是 GB18030,返回 1 是 UTF-8。
MySQL 迁达梦:
注意 LENGTH 语义相反。MySQL 的 LENGTH 数字节,等价于达梦的 LENGTHB;MySQL 的 CHAR_LENGTH 才对应达梦的 LENGTH。迁移 SQL 时这个函数名要换,换错了校验逻辑会全错。
这两个函数本身不复杂,但它们跟 VARCHAR(n) 的长度口径、字符集、跨库语义掺在一起。
库内校验:两个函数做校验,结果不一致,需要先确认列的定义是按字符还是按字节。
跨库迁移:符集不同时,LENGTHB 结果不可直接对比,需要先确认两端的字符集。
理清楚之后我的做法:校验脚本先查 UNICODE 和 LENGTH_IN_CHAR 参数,确定口径,再决定用 LENGTH 还是 LENGTHB。
文章
阅读量
获赞
