注册
达梦数据库性能调优
专栏/技术分享/ 文章详情 /

达梦数据库性能调优

LY 2026/08/28 190 1 0
摘要

达梦数据库性能调优

目录


本文章主要内容包括查看执行计划、索引优化、SQL优化与参数调优。

实验部分基础数据基于达梦数据库DMHR示例库。


1. 查看执行计划

1.1 EXPLAIN

返回树形文本计划,不实际执行SQL

  • 关键字段

    • cost:预估代价
    • rows:处理行数
    • bytes:字节数
  • 常见操作符

    • CSCN2:全表扫描(或聚簇索引扫描),通常是性能优化的重点对象。
    • SSEK2:二级索引扫描,一般效率较高。
    • BLKUP2:回表操作,根据索引找到行数据,如果量大会有随机IO开销。
    • SSCN:索引全扫描,不需要扫描表。
    • NSET2/PRJT2:结果集收集/投影操作,位于计划顶层,一般无需优化。
    • HASH2 INNER JOIN:HASH内连接。
    • INDEX JOIN SEMI JOIN:索引半连接。
EXPLAIN SELECT * FROM EMPLOYEE;

图片1.png


1.2 EXPLAIN FOR

以表格形式返回更详细的计划信息。

EXPLAIN FOR SELECT * FROM EMPLOYEE;

图片2.png


1.3 SET AUTOTRACE TRACE

会真实执行SQL,返回实际执行计划和统计信息。

SET AUTOTRACE TRACE; -- 开启 SET AUTOTRACE OFF; -- 关闭

图片3.png


1.4 查看内存中的执行计划

-- 查看全部执行计划 SELECT * FROM v$cachepln; -- 查看指定操作的查询计划 SELECT CACHE_ITEM, SQLSTR FROM V$CACHEPLN WHERE SQLSTR LIKE '%SQL语句片段%'; -- 导出执行计划 ALTER SESSION SET EVENTS 'IMMEDIATE TRACE NAME PLNDUMP, LEVEL 'CACHE_ITEM''; -- 清空执行计划 --为防止内存执行计划影响实验结果,后续实验初期执行清空语句清理内存(生产环境慎用) SP_CLEAR_PLAN_CACHE(); SP_CLEAR_PLAN_CACHE('CACHE_ITEM');

2. 优化器生成的执行计划与实际执行计划的区别

数据库为了提高性能,不会每次执行SQL都重新解析和优化,为了避免重复解析,会缓存SQL执行计划。如果数据库环境发生变化,但缓存计划没有及时更新,就会出现EXPLAIN查询的计划与实际执行的计划不一致。如:

  • 统计信息更新后,计划未刷新;
  • 数据库对象发生变更(对SQL涉及的表结构或索引进行了修改);
  • 使用了Hint或计划绑定;
  • 内存压力导致的计划淘汰。

2.1 绑定变量对实际执行计划的影响

绑定变量的首次执行计划会被缓存并强制复用,导致即使后续传入的参数值更适合其他执行路径,也会执行缓存的执行计划。SQL性能也可能严重退化。

(1)前置操作:创建表和数据

-- 创建一张表TEST_PLAN_MISMATCH,插入100000数据,其中99000条数据的STATUS值为1,1000条为2。 CREATE TABLE TEST_PLAN_MISMATCH ( ID NUMBER, STATUS NUMBER, CONTENT VARCHAR2(100) ); INSERT INTO TEST_PLAN_MISMATCH SELECT LEVEL, CASE WHEN LEVEL <= 99000 THEN 1 ELSE 2 END, 'TEST' FROM DUAL CONNECT BY LEVEL <= 100000; COMMIT; -- 在STATUS创建索引,但是由于STATUS列数据分布极度不均,当查询条件为STATUS=1时仍会进行全表扫描,当STATUS=2时会使用索引查询 CREATE INDEX IDX_PLAN_STATUS ON TEST_PLAN_MISMATCH(STATUS); -- 收集统计信息 DBMS_STATS.GATHER_TABLE_STATS( USER, 'TEST_PLAN_MISMATCH' );

(2)替换变量对执行计划的影响

SP_CLEAR_PLAN_CACHE(); DEFINE STATUS = 2; SELECT count(*) FROM TEST_PLAN_MISMATCH WHERE STATUS = &STATUS; DEFINE STATUS = 1; SELECT count(*) FROM TEST_PLAN_MISMATCH WHERE STATUS = &STATUS; SELECT * FROM v$cachepln;

图片4.png

替换变量会在编译前进行文本解析,实际执行的语句是解析替换的语句,每次值不同都重新生成新的执行计划。

(3)绑定变量/占位符对执行计划的影响

  • 使用占位符进行查询,输入参数1,由于STATUS = 1数据量偏大,执行全表扫描。

图片5.png

  • 再次执行查询,将参数改为2,STATUS = 2占比较小,优化器的执行计划应为索引查询,但是由于内存中缓存了STATUS = 1的执行计划,会复用执行计划。

图片6.png

  • 内存中只有一条执行计划。

图片7.png


2.2 统计信息更新后,计划未刷新导致内存中执行计划不一致

(1)当STATUS=2时,使用IDX_PLAN_STATUS索引查询

-- 执行查询,并且导出实际查询计划 SELECT * FROM TEST_PLAN_MISMATCH WHERE STATUS=2; SELECT CACHE_ITEM, SQLSTR FROM V$CACHEPLN WHERE SQLSTR LIKE '%SELECT * FROM TEST_PLAN_MISMATCH WHERE STATUS=2;%'; ALTER SESSION SET EVENTS 'IMMEDIATE TRACE NAME PLNDUMP, LEVEL 1896360288';

图片8.png

(2)更新表的数据,将STATUS=1:STATUS=2的比例修改为10000:90000

UPDATE TEST_PLAN_MISMATCH SET STATUS=2 WHERE ID<=90000; -- 更新统计信息 DBMS_STATS.GATHER_TABLE_STATS( USER, 'TEST_PLAN_MISMATCH' );

(3)当STATUS=2时,查看优化器的执行计划为全表扫描

EXPLAIN SELECT * FROM TEST_PLAN_MISMATCH WHERE STATUS=2;

图片9.png

(4)再次执行查询查看实际查询计划

此时仍使用缓存的执行计划,且对应语句只有一条查询记录,CACHE_ITEM相同。

SELECT * FROM TEST_PLAN_MISMATCH WHERE STATUS=2; SELECT CACHE_ITEM, SQLSTR FROM V$CACHEPLN WHERE SQLSTR LIKE '%SELECT * FROM TEST_PLAN_MISMATCH WHERE STATUS=2;%'; ALTER SESSION SET EVENTS 'IMMEDIATE TRACE NAME PLNDUMP, LEVEL 1896360288';

图片10.png


2.3 BEXP_CALC_ST_FLAG参数设置

在2.1中,列与绑定参数进行计算时,优化器会根据绑定的参数进行对应的调整。其相关参数BEXP_CALC_ST_FLAG配置如下。

(1)BEXP_CALC_ST_FLAG配置

优化器计算过滤条件选择率时,要不要额外考虑某些场景的开关,主要配置参数如下:

作用
0 使用默认策略,不进行调整
1 同一表不同列之间比较时进行调整
2 包含函数过滤条件时进行调整
4 同一表中AND/OR过滤条件进行调整
8 对CONTAINS过滤条件进行调整
16 单表过滤条件时进行调整
32 其他特定过滤场景
64 其他特定过滤场景
128 对列与绑定参数/值比较的过滤条件进行选择率调整
按位组合 BEXP_CALC_ST_FLAG=31,开启1+2+4+8+16

(2)BEXP_CALC_ST_FLAG = 0

不启用这些额外的选择率计算调整。

SELECT * FROM EMPLOYEE WHERE DEPARTMENT_ID = 101 -- 通过DEPARTMENT_ID=101预计返回多少行决定要不要走索引

(3)BEXP_CALC_ST_FLAG = 1

同一张表不同列之间进行比较。

SELECT * FROM EMPLOYEE WHERE DEPARTMENT_ID = MANAGER_ID -- 估算:A = B到底有多少行满足

(4)BEXP_CALC_ST_FLAG = 2

包含函数的过滤条件。

SELECT * FROM EMPLOYEE WHERE SUBSTR(job_id,1,1) = '5'; -- 针对函数过滤条件进行选择率计算调整

(5)BEXP_CALC_ST_FLAG = 4

同一张表中的AND/OR过滤条件。

EXPLAIN SELECT * FROM EMPLOYEE WHERE EMPLOYEE_NAME = '罗利平' OR EMPLOYEE_NAME = '王岳荪';

(6)BEXP_CALC_ST_FLAG = 8

针对CONTAINS条件。

CREATE CONTEXT INDEX IDX_EMP_NAME_CONTEXT ON EMPLOYEE(EMPLOYEE_NAME) LEXER DEFAULT_LEXER; EXPLAIN SELECT * FROM EMPLOYEE WHERE CONTAINS(EMPLOYEE_NAME, '姜');

(7)BEXP_CALC_ST_FLAG = 16

单表过滤条件。

(8)BEXP_CALC_ST_FLAG = 128

列与绑定参数/值进行比较时的选择率计算。


2.4 动态传参和固定值对内存影响

(1)创建表和数据

-- 创建表test_cache CREATE TABLE test_cache ( id INT PRIMARY KEY, flag INT, name VARCHAR(100), create_time DATE ); -- 插入100w条数据,其中flag=1占2%,2~4999是均匀低频值:每个值约200条 INSERT INTO test_cache SELECT LEVEL, CASE WHEN MOD(LEVEL, 10) = 1 THEN 1 ELSE MOD(LEVEL, 5000) END, 'USER_' || LEVEL, SYSDATE - LEVEL FROM DUAL CONNECT BY LEVEL <= 1000000; -- 在flag列创建索引 CREATE INDEX idx_test_flag ON test_cache(flag); -- 统计信息 DBMS_STATS.GATHER_TABLE_STATS( USER, 'TEST_CACHE' );

(2)使用JMeter模拟动态参数和固定参数在高并发条件下对内存的影响

  • 设置并发数为50,循环次数为500。

图片11.png

  • 设置flag为固定查询参数,值为随机生成的1~4999。

图片12.png

  • 查看内存使用情况。
SELECT name, SUM(TO_NUMBER(org_size))/1024.0/1024.0 AS sum_org_size_mb, SUM(TO_NUMBER(total_size))/1024.0/1024.0 AS sum_total_size_mb, SUM(TO_NUMBER(RESERVED_SIZE))/1024.0/1024.0 AS sum_RESERVED_SIZE_MB, SUM(TO_NUMBER(target_size))/1024.0/1024.0 AS sum_target_size_mb, SUM(n_extend_exclusive) sum_n_extend_exclusive FROM ( SELECT CASE WHEN name LIKE 'SHARE POOL%' THEN 'SHARE POOL' ELSE name END AS name, org_size, total_size, RESERVED_SIZE, target_size, n_extend_exclusive FROM v$mem_pool ) GROUP BY name ORDER BY 3 DESC, 4 DESC, 5 DESC;

图片13.png

  • 查看动态参数下内存使用情况。

图片14.png
图片15.png

结论:动态参数SQL支持计划复用、缓存高效;固定参数SQL引发大量硬解析、缓存膨胀。


3. 索引优化

3.1 最左前缀原则

联合索引必须要带上第一个字段才会生效(联合索引只有第一列才是有序的);不能跳过联合索引中间的列,会降低索引效果。

(1)查询的字段都在索引列当中,不用进行回表,减少查询次数

查询过程中尽量不要使用SELECT *

CREATE INDEX IDX1 ON EMPLOYEE (DEPARTMENT_ID, JOB_ID, SALARY); EXPLAIN SELECT DEPARTMENT_ID, JOB_ID, salary FROM EMPLOYEE WHERE DEPARTMENT_ID = 101 AND JOB_ID = '11' AND salary = 30000;

图片16.png

(2)查询语句满足最左前缀原则,扫描二级索引后回表查询

EXPLAIN SELECT * FROM EMPLOYEE WHERE DEPARTMENT_ID = 101 AND JOB_ID = '11' AND salary = 30000;

图片17.png

(3)不满足最左前缀,索引不生效

EXPLAIN SELECT * FROM EMPLOYEE WHERE JOB_ID = '11' AND salary = 30000;

图片18.png

(4)跳过中间列,索引效果降低

定位到EMPLOYEE_NAME后,再通过SLCT2过滤(因为索引跳过了hire_date列,salary无法用于精确定位)。

EXPLAIN SELECT * FROM EMPLOYEE WHERE DEPARTMENT_ID = 101 AND salary = 30000;

图片19.png


3.2 导致联合索引失效的操作

3.2.1 在索引列进行操作

在索引列进行如计算、函数、类型转换等操作,会导致索引失效。

CREATE INDEX IDX2 ON EMPLOYEE (EMPLOYEE_NAME); EXPLAIN SELECT * FROM EMPLOYEE WHERE LEFT(EMPLOYEE_NAME, 2) = '马学';

3.2.2 在索引列使用范围查询

在索引列使用范围查询,会导致右边的列失效。

EXPLAIN SELECT * FROM EMPLOYEE WHERE DEPARTMENT_ID > 101 AND JOB_ID = '11' AND salary = 30000;

图片20.png

3.2.3 不等、空值、OR会导致索引失效

在达梦数据库中,如果OR条件是针对同一个索引列(EMPLOYEE_NAME)的多个等值查询时,它会自动将这个OR转化为对索引的多次精确定位。

1. 不等值和空值:全表扫描

EXPLAIN SELECT * FROM EMPLOYEE WHERE EMPLOYEE_NAME != '马学铭'; EXPLAIN SELECT * FROM EMPLOYEE WHERE EMPLOYEE_NAME IS NULL;

2. OR条件针对同一个索引列

EXPLAIN SELECT * FROM EMPLOYEE WHERE EMPLOYEE_NAME = '马学铭' OR EMPLOYEE_NAME = '谢俊人';

图片21.png

3. OPTIMIZER_OR_NBEXP参数配置

OPTIMIZER_OR_NBEXP决定优化器面对A OR B时,是把OR拆开分别处理、整体处理,还是进一步做OR范围合并。

  • 参数取值如下:
含义
0 不进行OR优化
1 UNION_FOR_OR场景优化为无KEY比较方式
2 OR表达式优先作为整体处理
4 相关子查询中的OR也优先作为整体处理
8 OR布尔表达式的范围合并
16 同一列的范围条件+IS NULL优化
32 OR公因子包含子查询时,使用UNION_OR,避免特定情况下过滤条件无法下放
按位组合 OPTIMIZER_OR_NBEXP=31,开启1+2+4+8+16
-- 查看当前配置,默认配置为29:1+4+8+16 SELECT PARA_NAME, PARA_VALUE FROM V$DM_INI WHERE PARA_NAME = 'OPTIMIZER_OR_NBEXP';
  • OPTIMIZER_OR_NBEXP=0/1/2对比
-- 创建两个索引,使用OR查看数据 CREATE INDEX IDX_EMPLOYEE_NAME ON EMPLOYEE(EMPLOYEE_NAME); CREATE INDEX IDX_EMPLOYEE_EMAIL ON EMPLOYEE(EMAIL); EXPLAIN SELECT * FROM EMPLOYEE WHERE EMPLOYEE_NAME = '马学铭' OR EMAIL = 'chengqingwu@dameng.com';
  • OPTIMIZER_OR_NBEXP=0:分别使用两个索引。

图片22.png

  • OPTIMIZER_OR_NBEXP=1:转换成UNION FOR OR,使用"无KEY比较方式"。

图片23.png

  • OPTIMIZER_OR_NBEXP=2:作为整体处理。

图片24.png

  • OPTIMIZER_OR_NBEXP=4:与2相比,对相关子查询里面的OR,也优先按照整体方式进行优化。

  • OPTIMIZER_OR_NBEXP=8:OR范围合并。

SELECT * FROM T_ORDER WHERE SCORE BETWEEN 100 AND 300 OR SCORE BETWEEN 200 AND 500; -- 优化为 → SCORE BETWEEN 100 AND 500
  • OPTIMIZER_OR_NBEXP=16:同一列的范围条件+IS NULL。
SELECT * FROM T_USER WHERE AGE > 18 OR AGE IS NULL;
  • OPTIMIZER_OR_NBEXP=32:OR的公因子中包含子查询时,通过UNION_OR等方式优化,使过滤条件能够继续下推。

3.2.4 字符串不加引号

1. 字符串不加引号,会调用隐式转换进行全表扫描

CREATE INDEX INDEX01 ON EMPLOYEE(PHONE_NUM); EXPLAIN SELECT * FROM EMPLOYEE WHERE PHONE_NUM = '15312348552'; -- 将索引列转换为数值类型,导致该字段上的索引无法使用 EXPLAIN SELECT * FROM EMPLOYEE WHERE PHONE_NUM = 15312348552;

图片25.png

2. 隐式转换

隐式转换主要发生在不同数据类型的值进行比较、运算或赋值。

  • 字符串与数值混用:当VARCHAR类型字段与数值类型(如INT)进行比较或运算时,达梦数据库会尝试将字符串隐式转换为数值。
    • 如果被比较的字符串列建有索引,但查询条件中使用了数值(达梦会优先将索引列转换为数值类型,导致该字段上的索引无法使用)。
    • 如果VARCHAR列中存储了包含非数字字符(如字母、特殊符号)的数据,隐式转换为数值时会直接失败,抛出-6128: 无效的数字错误,中断业务流程。
SELECT * FROM EMPLOYEE WHERE EMAIL = 235465; -- -6128: 无效的数字
  • 字符串与日期、时间戳混用
CREATE TABLE T_ORDER ( CREATE_TIME DATE ); INSERT INTO T_ORDER VALUES ('2026-08-23'); SELECT * FROM T_ORDER WHERE CREATE_TIME = TO_DATE('2026-08-23', 'YYYY-MM-DD');
  • 不同数值类型之间混合运算:数据库可能进行数值类型提升/转换。

  • 两张表关联时,如果关联字段的类型不一致,也会导致索引失效。

CREATE TABLE t1( a1 INT, a2 VARCHAR(10) ); CREATE TABLE t2( b1 INT, b2 VARCHAR(10) ); INSERT INTO t1 VALUES(1,'1'),(2,'2'); INSERT INTO t2 VALUES(1,'1'),(2,'2'); CREATE INDEX indx1 ON t1(a2); EXPLAIN SELECT * FROM t1 JOIN t2 ON t1.a2 = t2.b1;

图片26.png

  • CASE表达式不同分支类型不一致,数据库需要进行类型转换。
SELECT CASE WHEN STATUS = 1 THEN '正常' -- VARCHAR ELSE 0 -- NUMBER END FROM T1;

3.2.5 LIKE百分号写最右

1. LIKE百分号写最右可能导致索引失效

EXPLAIN SELECT * FROM DMHR.EMPLOYEE WHERE EMPLOYEE_NAME LIKE '马%'; -- 索引生效 EXPLAIN SELECT * FROM DMHR.EMPLOYEE WHERE EMPLOYEE_NAME LIKE '%铭'; -- 全表扫描

2. LIKE_OPT_FLAG参数配置

达梦的LIKE_OPT_FLAG是动态、会话级参数。核心取值如下:

作用
0 不进行LIKE优化
1 对首尾存在通配符的LIKE,优化成POSITION();特殊情况下优化成REVERSE()
2 COL1 LIKE COL2
4 COL1 LIKE 'A'
8 可计算的LIKE表达式直接优化成常量
16 针对函数索引列的LIKE,优化成BETWEEN…AND…
按位组合 LIKE_OPT_FLAG=31,开启1+2+4+8+16
  • LIKE_OPT_FLAG=1:把LIKE优化成POSITION。
CREATE TABLE T_USER ( ID INT, NAME VARCHAR(100) ); INSERT INTO T_USER VALUES (1,'ZhangSan'),(2,'LiSi'),(3,'WangWu'),(4,'ZhangLi'),(5,'XiaoZhang'); SP_SET_PARA_VALUE(1, 'LIKE_OPT_FLAG', 1); CREATE INDEX IDX_NAME_POS ON T_USER(POSITION('Zhang', NAME)); EXPLAIN SELECT * FROM T_USER WHERE NAME LIKE '%Zhang%';

图片27.png

  • LIKE_OPT_FLAG=2COL1 LIKE COL2 || '%'
SELECT * FROM T WHERE NAME LIKE PREFIX || '%'; -- PREFIX表示另一列
  • LIKE_OPT_FLAG=4:将NAME LIKE 'A' || 'B%'优化成NAME LIKE 'AB%'
SP_SET_PARA_VALUE(1, 'LIKE_OPT_FLAG', 4); EXPLAIN SELECT * FROM T_USER WHERE NAME LIKE 'Zh' || 'a%';
  • LIKE_OPT_FLAG=8:表达式可以算出来,就提前算。与4类似,一个偏向拼接一个偏向计算。

  • LIKE_OPT_FLAG=16:LIKE → BETWEEN

UPPER(NAME) LIKE 'ABC%' → UPPER(NAME) >= 'ABC' AND UPPER(NAME) < 'ABD'

4. SQL优化

4.1 小表驱动大表

使用数据量较小、索引比较完备的表,然后使其索引和条件较大表进行数据筛选。

EXPLAIN SELECT E.EMPLOYEE_NAME, E.EMAIL, E.PHONE_NUM FROM DEPARTMENT D JOIN EMPLOYEE E ON E.DEPARTMENT_ID = D.DEPARTMENT_ID; -- 执行耗时: 7毫秒996微秒601纳秒 SELECT COUNT(E.EMPLOYEE_ID) FROM EMPLOYEE E JOIN DEPARTMENT D ON E.DEPARTMENT_ID = D.DEPARTMENT_ID; -- 执行耗时: 13毫秒878微秒100纳秒

4.2 用连接查询代替子查询

-- 子查询 EXPLAIN SELECT (SELECT DEPARTMENT.DEPARTMENT_NAME FROM DEPARTMENT WHERE DEPARTMENT.DEPARTMENT_ID = EMPLOYEE.DEPARTMENT_ID), EMPLOYEE.EMPLOYEE_NAME FROM EMPLOYEE; -- 执行耗时: 29毫秒61微秒100纳秒 -- 连接查询 EXPLAIN SELECT DEPARTMENT.DEPARTMENT_NAME, EMPLOYEE.EMPLOYEE_NAME FROM EMPLOYEE JOIN DEPARTMENT ON DEPARTMENT.DEPARTMENT_ID = EMPLOYEE.DEPARTMENT_ID; -- 执行耗时: 15毫秒718微秒100纳秒

ENABLE_RQ_TO_NONREF_SPL相关子查询参数配置

将相关查询表达式转化为非相关查询表达式,使相关查询从之前的平坦化方式转为一行一行处理。

  • 参数取值如下:
含义
0 不启用
1 优化查询项(SELECT列表)中的相关子查询
2 优化查询项+WHERE中的相关子查询
4 相关查询采用SPL去相关后,可以作为单表过滤条件
按位组合 ENABLE_RQ_TO_NONREF_SPL=7,开启1+2+4
  • ENABLE_RQ_TO_NONREF_SPL=0:不启用,将外层表和子查询表进行关联。
SP_SET_PARA_VALUE(1, 'ENABLE_RQ_TO_NONREF_SPL', 0); EXPLAIN SELECT e.employee_id, e.employee_name, e.department_id, ( SELECT AVG(e2.salary) FROM dmhr.employee e2 WHERE e2.department_id = e.department_id ) AS avg_dept_salary FROM dmhr.employee e;

图片28.png

  • ENABLE_RQ_TO_NONREF_SPL=1:只处理SELECT查询项里的相关子查询,让外层表逐行探测。

图片29.png

  • ENABLE_RQ_TO_NONREF_SPL=2:SELECT+WHERE中的相关子查询。1和2的区别主要在相关子查询在SELECT还是WHERE。
EXPLAIN SELECT /*+ ENABLE_RQ_TO_NONREF_SPL(2) */ e.employee_id, e.employee_name, e.department_id FROM dmhr.employee e WHERE EXISTS ( SELECT 1 FROM dmhr.employee e2 WHERE e2.department_id = e.department_id AND e2.salary > 20000 );

图片30.png

  • ENABLE_RQ_TO_NONREF_SPL=4:去相关之后作为单表过滤条件。
SELECT /*+ ENABLE_RQ_TO_NONREF_SPL(4) */ e.employee_id, ( SELECT MAX(e2.salary) FROM dmhr.employee e2 WHERE e2.department_id = e.department_id ) AS max_salary FROM dmhr.employee e WHERE e.department_id = 101;

图片31.png


4.3 提升GROUP BY的效率:为GROUP BY的字段设置索引

-- 添加索引前 EXPLAIN SELECT COUNT(EMPLOYEE_NAME) FROM EMPLOYEE GROUP BY DEPARTMENT_ID; -- 执行耗时: 13毫秒736微秒200纳秒 -- 在DEPARTMENT_ID添加索引 CREATE INDEX IDX_DEPARTMENT_ID ON EMPLOYEE (DEPARTMENT_ID); EXPLAIN SELECT COUNT(EMPLOYEE_NAME) FROM EMPLOYEE GROUP BY DEPARTMENT_ID; -- 执行耗时: 11毫秒265微秒300纳秒

4.4 批量插入

-- 1. 创建测试表 CREATE TABLE test_insert_efficiency ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, age INT, email VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 单条插入 INSERT INTO test_insert_efficiency (name, age, email) VALUES ('张三', 25, 'zhangsan@test.com'); INSERT INTO test_insert_efficiency (name, age, email) VALUES ('李四', 30, 'lisi@test.com'); INSERT INTO test_insert_efficiency (name, age, email) VALUES ('王五', 28, 'wangwu@test.com'); -- 执行耗时: 1毫秒396微秒200纳秒 -- 批量插入 INSERT INTO test_insert_efficiency (name, age, email) VALUES ('赵六', 35, 'zhaoliu@test.com'), ('孙七', 22, 'sunqi@test.com'), ('周八', 40, 'zhouba@test.com'); -- 执行耗时: 348微秒500纳秒

4.5 用UNION ALL代替UNION

  • UNION ALL:获取所有数据但不去重。
  • UNION:获取所有数据且数据去重,不包含重复数据。
SELECT EMPLOYEE.EMPLOYEE_NAME, EMPLOYEE.job_id FROM EMPLOYEE UNION ALL SELECT DEPARTMENT.DEPARTMENT_NAME, DEPARTMENT.location_id FROM DEPARTMENT; SELECT EMPLOYEE.EMPLOYEE_NAME, EMPLOYEE.job_id FROM EMPLOYEE UNION SELECT DEPARTMENT.DEPARTMENT_NAME, DEPARTMENT.location_id FROM DEPARTMENT;
评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服