1. 从“能用”到“会用”:为什么需要深入达梦SQL
如果你正在接触国产数据库,尤其是达梦数据库,那么“SQL”这个词对你来说一定不陌生。它就像是你和数据库之间沟通的“普通话”,无论是查询数据、更新记录,还是创建表结构,都离不开它。很多人觉得,SQL嘛,不就是SELECT * FROM table吗?我在MySQL、Oracle里都写过,换个数据库能有多大区别?直接上手不就行了。
我刚开始接触达梦时也是这么想的,结果很快就遇到了麻烦。一个在MySQL上跑得好好的分页查询,在达梦上性能却慢得离谱;一个简单的日期处理函数,语法报错让人摸不着头脑;更别提那些因为默认参数和兼容性设置不同而导致的“灵异”问题了。这让我意识到,把达梦SQL简单地等同于“标准SQL”或者“其他数据库的SQL”,是一个巨大的误区。达梦数据库在语法上高度兼容Oracle和SQL标准,这降低了入门门槛,但它的内核实现、优化器逻辑、特定函数以及大量为适应国产化环境而设计的特性,才是决定你能否高效、稳定使用的关键。
这篇内容,就是把我从“踩坑”到“填坑”过程中积累的那些最基础、也最容易出问题的达梦SQL知识梳理出来。它不是一份冰冷的命令手册,而是一份聚焦于“差异”和“实践”的生存指南。我们会避开那些在任何SQL教材里都能查到的通用语法,重点剖析那些达梦特有的细节、那些从其他数据库迁移过来时必然遇到的“坑”,以及如何写出在达梦上既正确又高效的SQL语句。无论你是刚刚接手一个达梦项目的新手,还是从其他数据库转战过来的老手,这些内容都能帮你快速建立起对达梦SQL的正确认知,少走弯路。
2. 连接与初探:你的第一个达梦SQL环境
在真正动手写SQL之前,一个稳定、可靠的连接环境是基础。达梦提供了官方管理工具DM管理工具,但对于习惯了Navicat、DBeaver等第三方工具的开发者来说,如何连接往往是个问题。
2.1 核心连接参数解析
无论使用哪种工具,连接达梦数据库都需要几个核心参数。理解它们背后的含义,能帮你快速定位连接失败的问题。
- 服务器地址与端口:最常见的地址是
localhost或127.0.0.1。端口默认为5236,这是达梦数据库服务实例监听端口。很多人在安装时会修改它,如果连接不上,首先检查端口是否正确。你可以通过查看达梦安装目录下dm.ini配置文件中的PORT_NUM参数来确认。 - 服务名:在达梦中,更常用的连接标识是“服务名”,而不是像MySQL那样的“数据库名”。一个达梦数据库实例下可以创建多个“模式”(Schema),连接时指定的服务名通常对应一个具体的模式。对于初学者,服务名常常就是
DMSERVER(默认实例名)或者你在安装时指定的名称。 - 用户名与密码:默认的系统管理员账户是
SYSDBA,其默认密码是SYSDBA。这是一个极其重要的安全须知:在生产环境中,首次安装后必须立即修改SYSDBA的密码。此外,达梦支持SYSAUDITOR(审计管理员)和SYSSSO(安全管理员)等其他系统预定义用户。
注意:很多连接失败源于对“服务名”和“模式”的混淆。在达梦里,你连接的是“数据库实例+服务名”,登录后操作的对象存在于某个“模式”下。
SYSDBA默认对应的模式是SYSDBA(与用户名相同)。创建新用户时,通常会关联一个同名的模式。
2.2 使用DBeaver连接达梦的实操细节
DBeaver因其开源和强大的多数据库支持,成为很多开发者的首选。用它连接达梦需要驱动。
- 下载达梦JDBC驱动:前往达梦官网,下载对应版本的
DmJdbcDriverjar包(如DmJdbcDriver18.jar)。注意驱动版本与数据库服务器版本的大致匹配(如JDBC 18对应DM8)。 - 在DBeaver中新建连接:选择数据库类型时,如果没有“Dameng”或“DM”,可以选择“Oracle”(因为驱动兼容),或者更通用地,选择“Generic” -> “JDBC”。
- 关键配置:
- JDBC URL:这是核心。标准格式类似于:
jdbc:dm://主机名:端口?schema=模式名&其它参数例如:jdbc:dm://localhost:5236?schema=SYSDBA - 驱动类名:
dm.jdbc.driver.DmDriver - 驱动文件:点击“添加文件”,将下载的
DmJdbcDriver18.jar添加进来。
- JDBC URL:这是核心。标准格式类似于:
- 常见连接错误与解决:
- “网络通信异常”:检查数据库服务是否启动(
systemctl status DmServiceDMSERVER或 Windows服务管理器),检查防火墙是否开放了5236端口。 - “登录失败”:核对用户名、密码、服务名。注意密码大小写。
- “驱动未找到”:确认驱动jar包路径正确,且DBeaver已正确加载。
- “网络通信异常”:检查数据库服务是否启动(
2.3 模式(Schema)与用户(User)的关系
这是达梦(以及Oracle)与MySQL、SQL Server概念上的一大区别,务必厘清。 在MySQL中,你创建一个数据库(CREATE DATABASE),然后在这个库下创建表。用户被授予访问某个库的权限。 在达梦中,核心的概念是“模式”。一个用户通常对应一个同名的模式。当你用SYSDBA登录,你当前的操作模式默认就是SYSDBA模式。你创建的表(如果没有指定模式)都会放在SYSDBA模式下。
-- 创建一个新用户,并同时创建其同名模式 CREATE USER "TEST_USER" IDENTIFIED BY "Test12345678"; -- 授予该用户连接等基本权限 GRANT "PUBLIC", "RESOURCE" TO "TEST_USER"; -- 切换到TEST_USER用户连接后,以下语句创建的表位于 TEST_USER 模式下 CREATE TABLE my_table (id INT);理解这一点至关重要,因为它影响着对象访问的SQL写法。当你要查询一个表时,可能需要指定模式名:SELECT * FROM SYSDBA.employee;或者SELECT * FROM TEST_USER.my_table;。
3. 数据定义语言(DDL):建表时的“达梦特色”
创建表结构是项目开始的第一步。达梦的CREATE TABLE语法兼容SQL标准,但有一些自己的扩展和默认行为,需要特别注意。
3.1 字段数据类型的选择与坑
达梦支持丰富的数据类型,有些与Oracle高度相似,有些则有细微差别。
字符类型:
CHAR(n): 定长字符串,n最大为8188。如果数据长度不足,会用空格填充。适用于长度固定的代码、标志位。VARCHAR(n): 变长字符串,n最大为8188。这是最常用的类型。注意,达梦的VARCHAR是以字符为单位计算长度,对于中文字符很友好。VARCHAR2(n): 与VARCHAR几乎完全相同,提供它主要是为了兼容Oracle用户的习惯。在达梦中,你可以把它们视为同义词。
实操心得:除非明确知道字段长度绝对固定且很短,否则一律使用
VARCHAR(n)。避免使用CHAR存储变长数据,因为末尾空格可能会在比较和拼接时带来意想不到的问题,例如WHERE code = ‘A’可能查不到CHAR(10)类型字段值为‘A’(后面有9个空格)的记录。数值类型:
NUMBER(p, s): 这是最通用、最精确的数值类型。p是精度(总位数),s是小数位数。例如NUMBER(10,2)表示总共10位,其中2位是小数。它可以存储整数、小数。INT,INTEGER: 等同于NUMBER(38,0),用于存储大整数。DECIMAL(p, s): 与NUMBER(p,s)功能相同。DOUBLE,FLOAT: 用于浮点数运算,可能存在精度损失。
建议:对于需要精确计算的金额、数量等字段,强烈推荐使用
NUMBER(p,s)。明确指定精度和小数位,可以避免浮点数精度问题,也便于数据库优化。日期时间类型:
DATE: 存储日期和时间,精确到秒。这是达梦默认的日期类型,也是最常用的。TIMESTAMP精度更高,但DATE在大多数业务场景下已足够。TIMESTAMP: 时间戳,精度可以到微秒(取决于定义,如TIMESTAMP(6))。
踩坑记录:很多从MySQL过来的人会习惯用
DATETIME,达梦里没有这个类型,对应的就是DATE。另外,达梦的DATE类型是包含时分秒的,这与一些数据库(如Oracle,其DATE也含时分秒)一致,但与某些数据库的DATE(仅日期)不同。
3.2 建表示例与关键子句
CREATE TABLE “EMPLOYEE” ( “ID” NUMBER(10) PRIMARY KEY, -- 主键 “EMP_NAME” VARCHAR2(100) NOT NULL, -- 非空约束 “SALARY” NUMBER(12, 2) DEFAULT 0, -- 默认值 “HIRE_DATE” DATE DEFAULT SYSDATE, -- 默认当前系统日期 “DEPARTMENT_ID” NUMBER(6), “EMAIL” VARCHAR2(255), “RESUME” TEXT, -- 大文本字段 -- 创建表时指定存储表空间(非必须,但生产环境建议) STORAGE ( INITIAL 64, -- 初始区大小 64K NEXT 32, -- 下一个区大小 32K ON “MAIN” -- 表空间名 ) ) TABLESPACE “MAIN”; -- 指定表所属表空间关键点解析:
- 双引号的使用:达梦默认对象名(表名、字段名)是不区分大小写的,但会被统一转换为大写存储。如果你希望保留大小写(例如字段名
empName),或者使用SQL保留字作为名称,必须使用双引号括起来。上述例子中使用了双引号,因此表名EMPLOYEE会以大写形式存储,但如果创建时写的是create table Employee,最终存储的也是EMPLOYEE。我个人的习惯是,除非有强制要求,否则不使用双引号,全部用大写或小写来写SQL,避免引号带来的混乱。 - 表空间:
TABLESPACE子句指定了表数据存储在哪个表空间。达梦安装后会创建MAIN、SYSTEM、ROLL等表空间。生产环境中,合理规划表空间(如将索引和表分开、按业务分表空间)对管理和性能有帮助。对于初学者,可以先使用默认的MAIN表空间。 - STORAGE子句:用于精细控制表的物理存储参数,如初始大小、扩展大小。对于小型或测试系统,可以不指定,使用默认值。对于已知会非常大的表,预先设置合理的
INITIAL和NEXT值可以减少存储碎片。
3.3 约束与索引的创建
约束保证了数据的完整性。
-- 创建表后添加约束 ALTER TABLE “EMPLOYEE” ADD CONSTRAINT “UK_EMP_EMAIL” UNIQUE (“EMAIL”); -- 唯一约束 ALTER TABLE “EMPLOYEE” ADD CONSTRAINT “FK_EMP_DEPT” FOREIGN KEY (“DEPARTMENT_ID”) REFERENCES “DEPARTMENT”(“ID”); -- 外键约束 -- 创建索引(非唯一) CREATE INDEX “IDX_EMP_DEPT” ON “EMPLOYEE”(“DEPARTMENT_ID”, “HIRE_DATE”);关于索引的实践建议:
- 主键和唯一约束会自动创建索引,无需手动再为这些字段建索引。
- 达梦支持多种索引类型:B树索引(默认)、位图索引(适用于低基数字段)、函数索引等。
- 创建复合索引时,将等值查询条件中最常用的字段放在最前面,范围查询字段放在后面。例如上例中,如果查询条件经常是
WHERE DEPARTMENT_ID = ? AND HIRE_DATE > ?,那么这个索引就是高效的。 - 不要盲目创建索引。索引会降低
INSERT、UPDATE、DELETE的速度。通常只为高频查询的WHERE条件、JOIN关联字段和ORDER BY字段创建索引。
4. 数据操作语言(DML):增删改查的差异点
基础的INSERT,UPDATE,DELETE,SELECT语法是标准的,但达梦在一些细节和函数上有所不同。
4.1 INSERT 的多种姿势
-- 1. 标准插入 INSERT INTO EMPLOYEE (ID, EMP_NAME, SALARY) VALUES (1, ‘张三‘, 8000); -- 2. 省略字段列表(不推荐,易出错) INSERT INTO EMPLOYEE VALUES (2, ‘李四‘, 9000, SYSDATE, 10, ‘lisi@xx.com‘, NULL); -- 3. 批量插入(性能关键!) INSERT INTO EMPLOYEE (ID, EMP_NAME, DEPARTMENT_ID) SELECT 100 + ROWNUM, ‘批量员工‘ || ROWNUM, 20 FROM DUAL CONNECT BY LEVEL <= 1000; -- 插入1000条测试数据 -- 达梦也支持 INSERT ALL 语法(兼容Oracle) INSERT ALL INTO EMPLOYEE (ID, EMP_NAME) VALUES (3, ‘王五‘) INTO EMPLOYEE (ID, EMP_NAME) VALUES (4, ‘赵六‘) SELECT * FROM DUAL;批量插入的重要性:在数据迁移或初始化时,务必使用批量插入(如上面的INSERT ... SELECT),而不是在程序循环中执行单条INSERT。单条提交会产生大量事务开销,速度可能相差百倍以上。达梦的dts(达梦迁移工具)或disql命令行工具的start transaction; ... commit;包裹多条INSERT也是批量操作。
4.2 UPDATE 与 DELETE 的注意事项
-- 更新数据 UPDATE EMPLOYEE SET SALARY = SALARY * 1.1, -- 涨薪10% HIRE_DATE = HIRE_DATE + 365 -- 日期计算 WHERE DEPARTMENT_ID = 10; -- 删除数据 DELETE FROM EMPLOYEE WHERE EMP_NAME = ‘张三‘; -- 清空表(高危操作!) TRUNCATE TABLE EMPLOYEE;TRUNCATEvsDELETE:
DELETE是DML操作,一行行删除,可以WHERE过滤,产生大量重做日志(Redo Log),速度慢,但可以回滚。TRUNCATE是DDL操作,直接回收表的数据段(高水位线复位),不产生大量日志,速度极快,不可回滚(在达梦中,如果启用了闪回功能,可能可以恢复,但不要依赖于此)。- 黄金法则:清空测试数据或整个表时用
TRUNCATE;删除部分业务数据时用DELETE,并务必带上WHERE条件,执行前最好先用SELECT确认要删除的数据。
4.3 SELECT 查询的核心:函数与分页
查询是SQL的重头戏。达梦内置了非常丰富的函数。
字符串函数:
SUBSTR,INSTR,REPLACE,LENGTH(字符数),LENGTHB(字节数),UPPER,LOWER,TRIM,LPAD/RPAD等。与标准SQL基本一致。日期函数:
SYSDATE: 当前系统日期时间。ADD_MONTHS(date, n): 加减月份,处理月末日期很智能。MONTHS_BETWEEN(date1, date2): 返回两个日期之间的月数。LAST_DAY(date): 返回日期所在月份的最后一天。TO_CHAR(date, ‘format‘): 日期转字符串,如TO_CHAR(HIRE_DATE, ‘YYYY-MM-DD HH24:MI:SS‘)。TO_DATE(string, ‘format‘): 字符串转日期,如TO_DATE(‘2023-10-01‘, ‘YYYY-MM-DD‘)。
注意:达梦的日期格式化模型与Oracle高度兼容。在处理字符串和日期转换时,明确指定格式符是避免错误的最佳实践。
分页查询——重中之重: 分页是Web应用中最常见的需求。达梦不支持MySQL的
LIMIT offset, row_count语法,也不直接支持SQL Server的OFFSET-FETCH(12c以后支持类似语法)。达梦主要支持两种分页方式:方法一:使用
ROWNUM(兼容Oracle,最常用)ROWNUM是一个伪列,表示结果集返回的行号,从1开始。-- 查询第6到第15条记录(每页10条,第2页) SELECT * FROM ( SELECT T.*, ROWNUM AS RN FROM ( SELECT ID, EMP_NAME, SALARY FROM EMPLOYEE ORDER BY HIRE_DATE DESC -- 最内层:业务查询和排序 ) T WHERE ROWNUM <= 15 -- 第二层:限制上限 ) WHERE RN >= 6; -- 最外层:限制下限为什么需要三层嵌套?因为
ROWNUM是在数据从表中取出后才分配的。WHERE ROWNUM <= 15可以筛选出前15行。但你不能直接写WHERE ROWNUM >= 6 AND ROWNUM <= 15,因为第一行ROWNUM=1不满足>=6,会被过滤掉,然后第二行变成新的ROWNUM=1,依然不满足,导致永远没有结果。所以需要先通过内层子查询固定住ROWNUM(别名为RN),再在外层用RN进行范围筛选。方法二:使用
LIMIT ... OFFSET(DM8版本开始支持,更直观)从达梦8版本开始,为了兼容MySQL/PostgreSQL生态,增加了LIMIT子句支持。SELECT ID, EMP_NAME, SALARY FROM EMPLOYEE ORDER BY HIRE_DATE DESC LIMIT 10 OFFSET 5; -- 从第6行开始(OFFSET 5),取10条如何选择?如果你的达梦版本是8.0及以上,并且不担心对Oracle兼容性的依赖,强烈推荐使用
LIMIT/OFFSET,它写法简洁,意图清晰。对于老版本或必须保证Oracle语法兼容的场景,则使用ROWNUM三层嵌套。
5. 高级查询与性能初窥
掌握了基础DML后,一些更复杂的查询和性能相关的基础概念需要了解。
5.1 连接查询(JOIN)
达梦支持标准的INNER JOIN,LEFT JOIN,RIGHT JOIN,FULL JOIN。写法与标准SQL一致。
-- 查询员工及其部门信息 SELECT E.EMP_NAME, D.DEPT_NAME FROM EMPLOYEE E LEFT JOIN DEPARTMENT D ON E.DEPARTMENT_ID = D.ID WHERE E.SALARY > 5000;性能提示:确保JOIN条件(ON子句)上的字段有索引。上述例子中,最好在EMPLOYEE.DEPARTMENT_ID和DEPARTMENT.ID上都有索引。
5.2 子查询与 EXISTS
子查询常用于WHERE或FROM子句中。
-- 查询比本部门平均工资高的员工 SELECT EMP_NAME, SALARY, DEPARTMENT_ID FROM EMPLOYEE E1 WHERE SALARY > ( SELECT AVG(SALARY) FROM EMPLOYEE E2 WHERE E2.DEPARTMENT_ID = E1.DEPARTMENT_ID -- 关联子查询 ); -- 使用EXISTS检查存在性(通常性能优于IN) SELECT * FROM DEPARTMENT D WHERE EXISTS ( SELECT 1 FROM EMPLOYEE E WHERE E.DEPARTMENT_ID = D.ID );关于INvsEXISTS:对于大数据集,如果子查询结果集小,而主查询结果集大,EXISTS通常效率更高(因为它找到一条匹配就返回)。反之,如果子查询结果集大,主查询结果集小,IN可能更合适。但这不是绝对的,实际执行计划取决于优化器。一个通用建议是:如果子查询能返回大量重复值,使用EXISTS;如果子查询结果集很小且唯一,两者差别不大,IN的写法可能更直观。
5.3 聚合与分组(GROUP BY)
-- 按部门统计人数和平均工资 SELECT DEPARTMENT_ID, COUNT(*) AS EMP_COUNT, AVG(SALARY) AS AVG_SALARY, SUM(SALARY) AS TOTAL_SALARY FROM EMPLOYEE WHERE HIRE_DATE > DATE ‘2020-01-01‘ GROUP BY DEPARTMENT_ID HAVING AVG(SALARY) > 8000 -- 对分组后的结果进行过滤 ORDER BY AVG_SALARY DESC;WHEREvsHAVING:记住一个简单规则——WHERE在分组前过滤行,HAVING在分组后过滤组。WHERE子句中不能使用聚合函数(如AVG(SALARY)),而HAVING可以。
5.4 视图的创建与使用
视图是一个虚拟表,基于SQL查询结果。
-- 创建一个视图 CREATE OR REPLACE VIEW V_EMP_DEPT AS SELECT E.ID, E.EMP_NAME, E.SALARY, D.DEPT_NAME FROM EMPLOYEE E JOIN DEPARTMENT D ON E.DEPARTMENT_ID = D.ID; -- 像使用普通表一样查询视图 SELECT * FROM V_EMP_DEPT WHERE DEPT_NAME = ‘研发部‘;视图的作用:简化复杂查询、提供数据安全层(只暴露视图中的字段)、保证逻辑一致性。需要注意的是,视图不存储数据,每次查询视图都会执行其背后的SQL语句。对于复杂的视图,可能会影响性能。
6. 事务控制与锁的初步理解
数据库事务是保证数据一致性的核心机制。达梦默认采用读已提交(READ COMMITTED)的隔离级别。
6.1 基本事务控制
-- 显式事务控制 START TRANSACTION; -- 或 BEGIN UPDATE ACCOUNT SET BALANCE = BALANCE - 100 WHERE USER_ID = ‘A‘; UPDATE ACCOUNT SET BALANCE = BALANCE + 100 WHERE USER_ID = ‘B‘; -- 此时其他会话看不到这两个UPDATE的结果 COMMIT; -- 提交事务,变更永久生效 -- 或 ROLLBACK; -- 回滚事务,所有变更撤销在达梦的图形工具或DBeaver中,通常默认是自动提交(AUTOCOMMIT)模式,即每条SQL语句都是一个独立的事务。在执行批量数据操作时,务必关闭自动提交,使用显式事务,将多个操作包裹在BEGIN和COMMIT之间,这不仅能保证原子性,还能大幅提升性能(减少日志刷盘次数)。
6.2 锁的简单认知与查询
当多个会话同时操作同一行数据时,锁机制防止数据混乱。达梦有行级锁、表级锁等。
UPDATE、DELETE、SELECT ... FOR UPDATE语句会对涉及的行加上排他锁(X锁)。- 普通的
SELECT语句在READ COMMITTED级别下不加锁,读取已提交的最新数据。
如何查询当前锁信息?这是排查“锁等待”或“死锁”问题的关键。达梦提供了系统视图V$LOCK和V$TRX来查看。
-- 查看当前锁信息(需要DBA权限) SELECT * FROM V$LOCK; -- 查看当前活动事务 SELECT * FROM V$TRX; -- 一个更实用的查询,查看谁锁住了谁 SELECT l.sess_id AS 阻塞会话ID, s.sess_seq AS 会话序列号, s.sql_text AS 正在执行的SQL, l.blocked AS 被阻塞的会话ID FROM V$LOCK l JOIN V$SESSIONS s ON l.sess_id = s.sess_id WHERE l.blocked > 0; -- blocked字段大于0表示该会话阻塞了其他会话如果你遇到一个SQL执行很久没反应,或者应用报“锁超时”错误,就可以通过这些视图来定位是哪个会话持有了锁,进而分析其SQL语句,判断是正常长事务还是异常锁等待。
避免长事务:长时间不提交的事务会持有锁,阻塞其他操作。在应用程序中,确保事务范围尽可能小,尽快提交或回滚。不要在事务内进行耗时的人工操作(如等待用户输入)。
7. 达梦SQL编写习惯与优化入门
最后,分享几个从其他数据库迁移到达梦,或在达梦上开发时需要养成的习惯和优化入门知识。
7.1 养成大小写一致的习惯
如前所述,达梦默认不区分对象名大小写并转为大写。为了避免混乱,建议团队统一规范:
- 方案A(推荐):所有SQL关键字大写,对象名和字段名小写(或大小写混合的驼峰命名),并且在创建对象时不使用双引号。这样在数据库中统一存储为大写,编写时用小写查询也能匹配。
select * from employee; - 方案B:如果确需保留大小写,则所有地方(创建、查询)都严格使用双引号。
select * from “Employee”;切忌混用,否则会出现“表或视图不存在”的错误。
7.2 使用绑定变量提升性能
这是编写高性能达梦SQL(乃至任何数据库SQL)的黄金法则。不好的写法(硬解析):
// 在循环中拼接SQL for (int id : idList) { String sql = “SELECT * FROM EMPLOYEE WHERE ID = ” + id; // 每次循环,数据库都要对全新的SQL字符串进行解析、优化,开销巨大。 }好的写法(软解析):
String sql = “SELECT * FROM EMPLOYEE WHERE ID = ?”; PreparedStatement pstmt = connection.prepareStatement(sql); for (int id : idList) { pstmt.setInt(1, id); // 数据库只需对带“?”的SQL解析一次,后续只需传递参数值,性能极大提升。 }达梦的优化器会对带绑定变量的SQL进行缓存,大大减少解析开销,同时还能防止SQL注入攻击。
7.3 初识执行计划
当你发现某条SQL很慢时,第一反应应该是查看它的“执行计划”。执行计划是数据库优化器决定的执行路径,告诉你它将如何访问数据(全表扫描?索引扫描?),以及操作的代价。 在达梦管理工具或DBeaver中,通常有“解释计划”或“执行计划”的功能按钮。你也可以使用SQL命令:
EXPLAIN SELECT * FROM EMPLOYEE WHERE DEPARTMENT_ID = 10 AND SALARY > 5000;或者更详细地:
CALL SP_EXPLAIN_INIT(); -- 初始化 EXECUTE IMMEDIATE ‘SELECT * FROM EMPLOYEE WHERE DEPARTMENT_ID = 10 AND SALARY > 5000‘; SELECT * FROM EXPLAIN; -- 查看计划看执行计划是个专业活,但初学者可以关注几个关键点:
CSCN2(全表扫描):如果对大表出现了这个,且没有有效的WHERE条件,通常意味着性能瓶颈。考虑为过滤字段添加索引。SSEK2(二级索引扫描)或CSEK2(聚簇索引扫描):这通常是好的,表示使用了索引。- 代价(COST):数值越大,预估成本越高。对比不同SQL或不同索引下的COST值,有助于判断。
- 操作顺序:从最内层(缩进最多)往最外层看,了解数据的获取流程。
7.4 慢SQL优化的第一步:索引与避免全表扫描
大多数慢SQL的根源在于不必要的“全表扫描”(FULL TABLE SCAN)。
- 为高频查询条件创建索引:分析你的应用SQL,找出
WHERE、JOIN ON、ORDER BY子句中最常出现的字段组合,为其创建复合索引。 - 避免在索引列上使用函数或运算:
WHERE UPPER(name) = ‘ABC‘会使索引失效。如果必须这样做,可以考虑创建函数索引:CREATE INDEX IDX_UPPER_NAME ON EMPLOYEE(UPPER(EMP_NAME)); - 谨慎使用
SELECT *:只查询需要的字段。特别是当表中有CLOB、BLOB等大字段时,SELECT *会带来巨大的网络和内存开销。 - 注意
LIKE查询:LIKE ‘ABC%‘(前缀匹配)可以使用索引,但LIKE ‘%ABC‘(后缀匹配)或LIKE ‘%ABC%‘(前后模糊)会导致索引失效,在数据量大时性能极差。考虑使用全文索引或其他搜索方案。
掌握这些基础,你已经能够应对达梦数据库80%的日常开发任务。真正的精通来自于在复杂场景下的实践、对执行计划的深入分析以及对达梦特有参数和特性的持续学习。记住,把达梦当作一个熟悉而又陌生的朋友,尊重它的特性,你就能和它高效合作。