news 2026/9/26 6:08:40

达梦DM8数据类型与运算符:从概念到建表实操

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
达梦DM8数据类型与运算符:从概念到建表实操

数据库技术基础系列笔记写到第9篇,终于可以聊点“动手”的内容了。前面几篇我们把关系模型、SQL语法骨架、事务和索引的概念都过了一遍,但概念归概念,真正坐到电脑前建库建表,第一个拦路的问题一定是:这个字段该用什么类型?这段查询该用哪个运算符?两个问题没搞清楚,后面写SQL全是坑。这次我用的是一套真实可装的国产数据库——达梦DM8,把它的数据类型和运算符从头到尾过一遍,附上写法示例和踩坑记录。

这篇笔记适合三类人:一是正在系统学数据库基础的学生,拿达梦当练手数据库;二是做Oracle/MySQL向达梦迁移的工程师,需要快速搞清楚类型对应关系;三是在做国产化适配、写兼容SQL的人。读完你能直接照着建表、写查询,不用再像翻官方文档那样字海茫茫地找答案。

1. 达梦DM8数据类型全景:先认清“出身”

达梦DM8在安装初始化实例的时候,会让你选择兼容模式,常见的是Oracle兼容和MySQL兼容。不管选哪个模式,SQL的大方向都走得通,但数据类型和某些运算符的行为会有细微差别。用个类比:达梦的SQL是一口“普通话”,但允许你说Oracle方言或者MySQL方言。很多人上手报错,不是达梦本身不行,而是拿MySQL的习惯去写Oracle方言下的SQL,两边对不上。

数据类型是建表的第一道工序,选错了后面全表都得改。DM8的数据类型体系整体上偏向Oracle风格,但又吸收了MySQL的一些便利类型,所以不能完全照搬任何一家的文档。

1.1 DM8与Oracle/MySQL的类型对照逻辑

先看一张对照表,这是迁移实战里最常用的一份速查。我把达梦、Oracle、MySQL三家的常用类型放在一起,这样从哪个生态过来都能一眼找到对应关系。

达梦DM8Oracle对应MySQL对应说明
NUMBER(p,s)NUMBER(p,s)无直接对应变长数值,按精度标度约束
INT/INTEGERNUMBER(10)INT/INTEGER普通整数
BIGINT/SMALLINT/TINYINT无BIGINT等整型族
DECIMAL/NUMERICNUMBERDECIMAL定点数,适合金额
FLOAT/DOUBLEBINARY_FLOAT/BINARY_DOUBLEFLOAT/DOUBLE浮点数
CHAR(n)CHAR(n)CHAR(n)定长字符串
VARCHAR(n)/VARCHAR2(n)VARCHAR2(n)VARCHAR(n)变长字符串
CLOB/TEXTCLOBTEXT大文本
BLOBBLOBBLOB二进制大对象
DATEDATE(含时分秒)DATE(仅到天)日期时间
TIMESTAMP(p)TIMESTAMP(p)DATETIME高精度时间戳
BIT无BOOLEAN/TINYINT(1)布尔/位

注意一个最容易被忽略的点:DM8的DATE类型是包含时分秒的,这和Oracle一致,但和MySQL的DATE(只到天)完全不同。从MySQL迁过来的人,常常发现日期字段插进去再查出来多了时间部分,或者查询条件对不上,根源就在这。

1.2 数值类型:精度、标度与选型

达梦的数值类型核心是NUMBER,可以写NUMBER或者NUMBER(p,s)。p叫精度,表示总共有多少位有效数字;s叫标度,表示小数部分保留几位。比如NUMBER(8,2),表示整数部分最多6位,小数2位,总共8位。超过标度时达梦会按四舍五入处理,而不是直接报错,这一点在写入前要有预期。

实际选型我给一套直接抄的经验:状态码、布尔标记用TINYINT或SMALLINT;业务主键用INT或BIGINT,看数据量预估,别省;金额、单价、费率一律用DECIMAL(18,2)这类定点数,绝对不要用FLOAT或DOUBLE存钱。原因很简单,浮点数的二进制表示无法精确表达很多十进制小数,0.1存进去可能变成0.1000000000000000055,单笔看没感觉,累计几百笔误差就出来了。打个比方,浮点数像用勺量水,定点数像用量杯,量杯误差可控。

建表时我习惯把金额精度放大一档,比如DECIMAL(18,4)存储、展示时再四舍五入到2位,这样中间计算环节的误差更小,最后输出的精度也够。还有一个细节,达梦的DECIMAL和NUMBER在绝大多数场景下是同义的,所以写DECIMAL(10,2)和NUMBER(10,2)差别不大,关键是精度要留够。

提示:在达梦的某些严格参数模式下,超精度写入会直接报错而不是四舍五入。建议建表前先确认实例的参数设置,别赌默认行为。

1.3 字符类型:长度单位是字节的坑

达梦的CHAR(n)、VARCHAR(n)、VARCHAR2(n),其中n的单位默认是字节,不是字符数。这一点坑过无数从MySQL迁过来的人。MySQL的VARCHAR(20)是指20个字符,而达梦默认的VARCHAR(20)是20个字节。在UTF-8字符集下,一个汉字占3个字节,VARCHAR(10)只能存3个汉字加1个字节的余量,想存10个汉字必然报“字符串超出长度”之类的错误。

解决办法有两个:一是在初始化实例时把参数LENGTH_IN_CHAR设为1,这样后续定义字符串长度就按字符数计算;二是如果实例已经建好,定义字段时按“预计汉字数乘以3”来预留字节数。我个人更推荐前者,从根上消除换算负担,但要注意这个参数是在初始化数据库实例时确定的,后期改起来比较麻烦。

CHAR和VARCHAR(2)的选择也有讲究。CHAR是定长,存“abc”也会占满10个字节,不足部分补空格;取出时尾部空格通常会被忽略,但参与字符串比较或拼接时容易出意外。VARCHAR2是变长,存多少占多少,绝大多数业务字段都该用它。只有像身份证号、订单号这类长度绝对固定的场景,用CHAR才有意义。

大文本字段方面,CLOB和TEXT在达梦里都能存长文本,区别主要是使用习惯。从Oracle过来就用CLOB,从MySQL过来就用TEXT,两者在达梦的底层映射基本一致。二进制数据用BLOB,比如图片、文件流,直接以二进制形式存储。

1.4 日期时间类型与常用函数

达梦的日期时间类型主要有DATE、TIME、TIMESTAMP(p)、DATETIME。DATE包含年月日时分秒,默认精度到秒;TIMESTAMP可以带小数秒,比如TIMESTAMP(3)精确到毫秒,适合记录流水时间、日志时间。DATETIME在达梦里基本等同TIMESTAMP,写哪个都行。

日期时间相关的函数在实操里用得非常多。最常用的有:SYSDATE取当前数据库时间;TO_DATE把字符串按格式转成日期;TO_CHAR把日期转成指定格式的字符串。写法示例:

SELECT SYSDATE FROM DUAL; -- 当前日期和时间 SELECT TO_DATE('2025-06-01 10:30:00', 'yyyy-mm-dd hh24:mi:ss') FROM DUAL; -- 字符串转日期 SELECT TO_CHAR(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') FROM DUAL; -- 日期转字符串,hh24表示24小时制

日期可以直接做加减法,加1就是加一天,减0.5就是减半天。这个特性在做到期日、逾期天数计算时非常顺手。例如查3天前的数据,直接写SYSDATE - 3。格式掩码里的hh24和mm特别容易写反,很多人想取分钟却写了mi,导致永远拿到月份,这个我踩过不只一次。

2. 运算符:从语法规则到执行语义

数据类型解决“存什么”,运算符解决“怎么算”。这一部分看着简单,实际是SQL写错的重灾区。达梦支持算术、比较、逻辑、字符串连接、集合运算等几类运算符,其中NULL与运算符的交互最值得花时间搞清楚。

2.1 算术运算符与除法精度边界

达梦的算术运算符就是标准的加+、减-、乘*、除/,加上取余相关的能力。最常用的场景是金额计算、数量汇总、比率计算。比如:

SELECT price * quantity AS total_amount, price * 0.9 AS discount_price, quantity / 100 AS convert_count FROM t_order_detail;

除法的精度行为值得专门说。整数除以整数,在达梦里结果是精确的小数数值,比如1/2得到0.5,不是MySQL旧版本里的0。如果你确实想要整除的效果,需要用FLOOR或者TRUNC处理,而不是指望/自动截断。

除零错误是经典的运行时错误。达梦里除零会报错,SQL执行直接中断。实际开发中,除法的分母经常来自字段值,有时为空有时为0,稳妥做法是用NULLIF把0转成NULL,因为任何数除以NULL的结果是NULL,不会报错:

SELECT price / NULLIF(quantity, 0) FROM t_order_detail; -- quantity为0时结果为NULL,而不是报错

取余运算用MOD函数,比如MOD(17, 5)结果为2。达梦在兼容MySQL方言时也支持%写法,但为了跨模式统一,我建议一律写MOD。

2.2 比较运算符与NULL三值逻辑

比较运算符包括=、<>、!=、<、>、<=、>=、BETWEEN AND、IN、LIKE、IS NULL。这些在语法上跟其他数据库没有太大区别,但NULL参与比较时的结果,必须建立正确的心智模型。

SQL的逻辑不是二值逻辑,而是三值逻辑:TRUE、FALSE、UNKNOWN。NULL参与任何比较运算,结果都是UNKNOWN。比如NULL = NULL的结果是UNKNOWN,NULL > 5的结果也是UNKNOWN。WHERE子句只保留结果为TRUE的行,所以写了WHERE name = NULL,会一条数据都查不出来,因为所有行的比较结果都是UNKNOWN,不是FALSE,更不是TRUE。

正确地判断NULL只能用IS NULL或IS NOT NULL:

SELECT * FROM t_user WHERE name IS NULL; -- 查name为空的用户

NOT IN和NULL的搭配是另一个高频坑。当NOT IN的子查询结果集中包含NULL时,整个条件的结果会变成UNKNOWN,导致一条数据都查不到。比如:

SELECT * FROM t_user WHERE dept_id NOT IN (SELECT dept_id FROM t_dept); -- 如果t_dept.dept_id存在NULL,这个查询结果为空

原因是NOT IN等价于一连串的AND条件:dept_id <> 值1 AND dept_id <> 值2 ...,一旦其中某个值碰上是NULL,AND条件里出现UNKNOWN,整体就无法为TRUE。解决办法是用NOT EXISTS替代,或者先过滤掉子查询里的NULL。这是我强烈建议记在本子上的一个经验,生产环境里因为这个查不出数据的问题,排查成本往往很高。

LIKE运算符也要说一下。%匹配任意多个字符,匹配单个字符。如果模式本身包含%或,需要用ESCAPE指定转义符,比如LIKE '100_%' ESCAPE '',表示匹配以“100_”开头的字符串。实际业务里,模糊搜索最多的就是LIKE '%关键字%',看起来简单,但要注意通配符开头的写法会导致索引失效,数据量大时性能会很难看。

2.3 逻辑运算符AND OR NOT与短路判断

逻辑运算符用在WHERE、HAVING、CASE WHEN等条件里,优先级从高到低是NOT、AND、OR。这个优先级顺序看似简单,实际经常写错。比如WHERE a = 1 OR a = 2 AND b = 3,实际执行的是a = 1 OR (a = 2 AND b = 3),而不是(a = 1 OR a = 2) AND b = 3。为了避免歧义,复杂的逻辑条件一定加括号,括号是不要钱的,可读性却值钱得多。

AND、OR与NULL组合的结果,是SQL逻辑里最容易绕晕的地方,这里直接给一张真值表:

左值右值AND结果OR结果
TRUETRUETRUETRUE
TRUEFALSEFALSETRUE
TRUENULLNULLTRUE
FALSENULLFALSENULL
NULLNULLNULLNULL

记法很简单:AND碰到FALSE就是FALSE,OR碰到TRUE就是TRUE,其它情况遇到NULL就是NULL。

所谓短路,是指逻辑运算在结果已经确定时不再计算后面的表达式。SQL标准里没有强制规定表达式求值顺序,数据库优化器可能调整执行计划,所以千万不要依赖短路去保护某个函数的合法性。比如写了CASE WHEN n > 0 THEN 1/n ELSE 0 END,理论上应该安全,但实际执行时不一定按你想象的顺序评估子表达式,更稳妥的写法是CASE WHEN n = 0 THEN 0 ELSE 1/n END,把除法安全兜住。

2.4 字符串连接与其他补充运算符

字符串连接运算符在达梦里是双竖线||,例如SELECT first_name || ' ' || last_name FROM t_user。这是Oracle风格的写法,也是达梦默认推荐的写法。从MySQL过来的人可能习惯CONCAT函数,达梦也支持CONCAT和CONCAT_WS,但在Oracle兼容模式下,||最通用。

集合运算也是SQL里一类特殊的运算符:UNION、UNION ALL、MINUS/EXCEPT、INTERSECT。UNION去重,UNION ALL不去重,需要全量合并时优先用UNION ALL,避免无谓的排序去重开销。MINUS和INTERSECT在达梦里都可以用,语义和其他数据库一致。

位运算在达梦里也有支持,包括&、|、^、~、<<、>>这类位运算符,但要注意兼容模式的影响。Oracle风格下没有那么多现成的位运算符,通常用BITAND函数处理按位与;MySQL兼容模式下使用&和|更顺手。实际业务里直接用位运算的场景不多,大多是标志位设计时用到,这里知道有这回事就行,不用深挖。

3. 类型转换与兼容模式实操

类型转换是“数据类型”和“运算符”两块内容的交叉点。SQL里比较和计算时,两边的类型不一致怎么办?数据库要么自动转,要么报错。搞清达梦的转换规则,才能避免一堆莫名其妙的运行时错误。

3.1 显式转换函数与隐式转换规则

达梦的显式类型转换有三种常用手段:CAST、CONVERT、TO系列函数。CAST是通用写法,语法清晰:CAST(expr AS target_type)。CONVERT的功能类似,但参数顺序在不同数据库里有差异,跨库移植时要留心。TO_CHAR、TO_NUMBER、TO_DATE则是把值往特定类型转的专用函数。

真实项目里最常用的还是TO系列,因为带格式控制。TO_CHAR可以把数字转成指定格式的字符串,比如TO_CHAR(12345.6, '999,999.99');TO_DATE可以把字符串按指定日期格式解析;TO_NUMBER则把字符串转成数字,比如TO_NUMBER('123.45')。

隐式转换是数据库自动完成的类型转换。比如数字字段和字符串比较时,达梦会尝试把字符串转成数字再比;字符串和日期比较时,会尝试把字符串解析成日期。隐式转换的优点是省事,缺点是埋雷。最典型的问题:字段上有索引,但WHERE条件的写法触发了隐式转换,导致索引失效,全表扫描。比如字符串类型的字段code,你写WHERE code = 123456,数据库会把每行的code转成数字去比较,索引就用不上了。正确的写法是WHERE code = '123456',保持类型一致。

我的经验是:能用显式转换的地方就不要依赖隐式转换,写SQL时把类型对齐,既能保护性能,也能让语义更清晰。

3.2 兼容模式下写SQL的差异

达梦兼容Oracle和MySQL两套主流方言,这个能力在做国产化替代时非常实用。但“兼容”不等于“完全一致”,实际使用中要留意差异,避免写出换一个模式就跑不了的SQL。

Oracle兼容模式下,可以用DUAL表、SYSDATE、||字符串连接、ROWNUM伪列,这些是Oracle开发者的习惯。MySQL兼容模式下,LIMIT分页、反引号引用标识符、AUTO_INCREMENT自增列这些写法可以用。

我建议在项目里确定一个主兼容模式,所有SQL按这个模式的规范来写,不要混用。比如开发团队大部分来自MySQL背景,那初始化实例时就选MySQL兼容,日常写LIMIT、AUTO_INCREMENT都顺畅;如果团队来自Oracle背景,就选Oracle兼容,写ROWNUM和DUAL更顺手。最忌讳的是今天写LIMIT明天写ROWNUM,同一个项目里两种分页风格并存,后面维护的人会崩溃。

分页查询这块我多说一句:Oracle兼容模式下的标准分页是三层嵌套子查询配ROWNUM,写起来繁琐但稳定;MySQL兼容模式下直接LIMIT offset, count。达梦的新版本也支持了一些简化的分页语法,但统一按主兼容模式的规范写最稳。

3.3 用Navicat连接DM8与实践验证

光看语法不够,落地验证一下。我用Navicat连接达梦DM8,把数据类型和运算符过了一遍,这里直接给连接步骤和验证SQL。

Navicat连接达梦,在连接对话框里选择“达梦”类型,主机填127.0.0.1,端口填达梦默认的5236,用户名用SYSDBA,密码是安装实例时设置的。如果Navicat提示缺少驱动,需要在工具菜单的驱动管理里添加达梦JDBC驱动包。驱动从达梦官网下载,是一个DmJdbcDriver18.jar之类的文件,添加后重启连接就正常了。

连接成功后,建一张覆盖多种类型的测试表:

CREATE TABLE t_practice ( id INT IDENTITY(1,1) PRIMARY KEY, name VARCHAR2(50), age TINYINT, score DECIMAL(6,2), birthday DATE, create_time TIMESTAMP DEFAULT SYSDATE );

注意IDENTITY是达梦的自增列写法,建表时不需要手动插id值。插入几条数据:

INSERT INTO t_practice (name, age, score, birthday) VALUES ('张三', 25, 88.50, TO_DATE('2000-03-15', 'yyyy-mm-dd')); INSERT INTO t_practice (name, age, score, birthday) VALUES ('李四', 30, 76.00, TO_DATE('1995-08-20', 'yyyy-mm-dd'));

查询时把运算符都用上,验证一下行为:

SELECT id, name, age + 1 AS next_age, score * 0.9 AS discount_price, TO_CHAR(birthday, 'yyyy-mm-dd') AS birthday_str FROM t_practice WHERE age >= 18 AND score > 70 ORDER BY score DESC;

这条SQL覆盖了算术运算符、比较运算符、逻辑运算符、TO_CHAR转换和ORDER BY排序,跑通之后数据类型和运算符的基础应用就算入门了。想验证表达式正确性,可以用DUAL直接算:

SELECT 1 + 1 AS r1, MOD(17, 5) AS r2, 'a' || 'b' AS r3 FROM DUAL;

结果分别是2、2、ab,一目了然。

4. 常见问题与排查实录

实操中遇到的问题,比语法本身更能加深理解。我把这段时间在达梦上反复遇到的典型问题整理成速查表,再挑三个最典型的案例拆开讲,每个都是真实场景,定位思路和解决方案都可以直接复用。

4.1 高频报错速查表

报错现象典型原因处理方法
字符串超长/数据过长VARCHAR长度按字节计算,中文占多字节预留字节数,或把LENGTH_IN_CHAR设为1
无效的数字/无法从字符串转换为数字隐式转换时字符串里混了非数字字符用TO_NUMBER显式转换,先清洗数据
除数不能为0算术运算除数为0用NULLIF(denominator, 0)规避
WHERE条件查不出数据写了= NULL或NOT IN带NULL子查询改用IS NULL,或把NOT IN换NOT EXISTS
日期格式无效TO_DATE的格式掩码与实际字符串不匹配统一用yyyy-mm-dd hh24:mi:ss这类标准掩码
列名或表名找不到引号、大小写敏感导致标识符不匹配建表时统一大小写,字符串常量和标识符分清
主键冲突自增列使用不当或重复插入检查IDENTITY设置,确认插入时不手填id

4.2 三个容易踩坑的真实案例

第一个案例是WHERE name = NULL。SQL执行没报错,但结果集是空的。很多初学者误以为NULL是“空值”,用=去判断,看着合理,实则完全错误。正确写法是IS NULL。这个案例在培训课堂上每次都有不少人中招,核心还是没建立起三值逻辑的心智模型。

第二个案例是NOT IN配合子查询结果集。表t_user里有500条记录,执行WHERE dept_id NOT IN (SELECT dept_id FROM t_dept)却返回0行,而t_dept里明明只有3个部门,500条记录应该大部分都查得出来才对。排查后发现t_dept.dept_id列里有NULL值,整个条件就被NULL污染了。改成NOT EXISTS或者先在子查询里过滤掉NULL,问题立刻解决。这个案例的价值在于:报错不是唯一的问题形态,查不出数据更隐蔽。

第三个案例是VARCHAR2(10)存中文超长。从MySQL迁移的同事习惯性写VARCHAR2(10),然后往里插“数据库基础”这五个字,直接报字符串超长。UTF-8下一个汉字3字节,5个汉字要15字节,10不够用。这不是达梦的问题,是长度单位没搞清楚。同一个字段按字符算改成VARCHAR2(15)或者初始化时打开LENGTH_IN_CHAR,问题就没了。

4.3 排查思路与工具方法

报错之后的排查顺序,我建议先看错误码和错误文案,达梦的错误信息里通常直接带了原因,比如“无效的数值”指向类型转换,“数值超出范围”指向精度问题。文字不够清楚时,去达梦安装目录下的日志目录里翻实例日志,里面会记录更详细的上下文。

SQL层面的排查有个小技巧:把复杂查询拆成最小表达式,用DUAL一个个验证。比如怀疑隐式转换有问题,就单独执行SELECT TO_NUMBER('123abc') FROM DUAL,直接复现错误,比抱着整条大SQL猜快得多。性能问题则用EXPLAIN看执行计划,确认是否触发全表扫描、有没有用上索引。

调试期间建议和数据打交道时保留现场:把出错的SQL、参数、表结构都留档,方便对照排查。很多时候问题不是单一原因,比如既字段长度不够、又有类型隐式转换、还有NULL参与计算,三个因素叠加,一层层拆开才看得清楚。

这套笔记整理下来,我最大的体会是:达梦DM8并不是另一套完全陌生的东西,它的数据类型骨架和Oracle高度相似,运算符体系也符合SQL标准,真正让人栽跟头的往往就集中在字节长度、NULL语义、隐式转换和兼容模式这几个点上。刚开始学的人容易按MySQL的习惯写,导致类型不对、NULL判断出错;迁移过来的人又容易忽略字节与字符的差异,在中文数据上反复踩坑。把类型对照表、运算符优先级和NULL处理的规则打印出来贴在显示器旁边,写上一个月,这些坑基本就都能绕开了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 6:08:21

通信系统排队论实战:从M/M/1到M/G/1的工程落地

简介&#xff1a;本资源是《通信网基础》课程第7章核心讲义&#xff0c;系统讲解排队论的基本概念与建模方法&#xff0c;面向通信工程、网络工程及相关专业本科生与研究生&#xff0c;助力理解通信系统性能分析的理论根基。内容涵盖排队系统的四大构成要素&#xff08;到达过程…

作者头像 李华
网站建设 2026/9/26 6:07:54

Claude CLI 工作流骨架:MCP协议+Node.js+NPM工程化实践

1. 项目概述&#xff1a;这不是一个“模板库”&#xff0c;而是一套面向 Claude 开发者的 CLI 工作流骨架“claude-code-templates”这个名称乍看像是一堆静态代码片段的集合&#xff0c;但实际在开发者社区里&#xff0c;它指代的是一套围绕 Anthropic Claude 模型构建、可直接…

作者头像 李华
网站建设 2026/9/26 6:07:41

区域影像中心建设:DICOM网关与Ceph存储落地实践

简介&#xff1a;本资源是一份面向医疗信息化建设者的区域医学影像中心系统建设方案书&#xff0c;适用于卫健委、区域医联体、基层医院信息科及PACS系统集成商等角色&#xff0c;聚焦解决基层影像诊断能力薄弱、报告质量参差、跨机构数据共享难、患者重复检查等问题。方案以WS…

作者头像 李华
网站建设 2026/9/26 6:07:03

四路CAN FD+云调试:汽车电子多总线调试与逆向工程实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华