注册
sql语法学习
专栏/技术分享/ 文章详情 /

sql语法学习

wwwwww 2026/09/18 592 0 0
摘要

数据类型

数值类型

精确数值类型
这类类型用于存储精确的数值,不会发生舍入误差,非常适合金融、金额等对精度要求极高的场景。

  • NUMERIC(p,s),DECIMAL(p,s),NUMBER:两者功能和用法相同,可以看作同一个类型。
    • 说明:精度最高的数值类型,p 代表总精度(位数),s 代表标度(小数位数)。如果其值超出进度,返回出错信息,如果超出标度,则对多余的位会被四舍五入截断。当指定精度而不指定标度时,标度为 0,相当于整数。如果不指定精度和标度,标度根据缺省精度和输入值的小数位动态调整。缺省精度由 INI 参数 DECIMAL_ENHANCED 决定,标度不应大于其精度。当 DECIMAL_ENHANCED=1,缺省精度为 40;当 DECIMAL_ENHANCED=0,缺省精度为 38。
    • 特点:存储的值完全精确,不会丢失精度。如果未指定精度和标度,默认行为取决于具体上下文,但通常建议显式定义。
    • 精度范围:1到38位(正负数),标度不应大于精度。在较新版本加入了 DECIMAL_ENHANCED 参数,如果其为1,在精度和标度都为0的情况下精度可以到40。

近似数值类型
这类类型基于 IEEE 754 标准,存储的是浮点数的近似值,适合科学计算、统计等对绝对精度要求不高但需要处理极大或极小数值范围的场景。

  • FLOAT§

    • 说明:浮点数类型。p 表示二进制精度。
    • 特点:根据p的值会根据 p 的值,达梦会自动映射到 REAL 或 DOUBLE PRECISION。若 p 在 1 到 24 之间,DM 将其转换为标准 C 语言中的 REAL 类型。若 p 在 25 到 53 之间,转换为标准 C 语言中的 DOUBLE 类型。
    • 取值范围:-1.797693134862315810^308 ~ 1.797693134862315810^308。
  • REAL

    • 说明:单精度浮点数。
    • 特点:7 位十进制有效数字。
    • 取值范围:-3.410^38 ~ 3.410^38。
  • DOUBLE PRECISION

    • 说明:双精度浮点数。
    • 特点:同DOUBLE。
  • DOUBLE§

    • 说明:二进制精度的浮点数
    • 特点:直接使用标准 C 语言中 DOUBLE。精度与取值范围与 FLOAT 一样。

整数类型
用于存储没有小数部分的数值。

  • SMALLINT

    • 范围:-32,768 到 32,767。
    • 存储:2 字节。
  • INTEGER / INT

    • 范围:-2,147,483,648 到 2,147,483,647。
    • 存储:4 字节。
    • 说明:这是最常用的整数类型。
  • BIGINT

    • 范围:-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。
    • 存储:8 字节。
    • 说明:用于存储非常大的整数。
  • TINYINT

    • 范围:0 到 255。
    • 存储:1 字节。

字符类型

  • CHAR

    • 说明:n 表示固定长度。
    • 特点:如果实际存储的字符串长度小于 n,系统会自动用空格填充至 n 的长度。在比较时,达梦通常会忽略尾部空格(取决于具体配置和上下文),但在存储时确实占用了空间。
    • 适用场景:存储长度固定的代码、状态码、国家代码等(如 ‘CN’, ‘001’)。
  • VARCHAR(n) / VARCHAR2(n)

    • 说明:n 表示最大字符长度。在达梦中,VARCHAR 和 VARCHAR2 是完全等价的。
    • 特点:存储实际长度,不占用多余空间。
    • 适用场景:存储姓名、地址、描述等长度不固定的文本。

日期时间类型

  • DATA:
    • 说明:包含年、月、日信息。插入含时间的值会被截断,只保留日期。可使用 CURRENT_DATE 等获取当前日期。
    • 特点:仅存储日期,无时区概念。
    • 范围:-4712-01-01到9999-12-31
  • TIME:
    • 说明:包含时、分、秒信息。
    • 特点:仅存储时间,无时区概念。
    • 范围:00:00:00.000000 至 23:59:59.999999;可指定小数秒精度 (0-6)。
  • TIMESTAMP:
    • 说明:包括年、月、日、时、分、秒信息。
    • 特点:无原生时区支持。达梦提供 TIMESTAMP WITH TIME ZONE 和 WITH LOCAL TIME ZONE 用于时区处理,但原生的 TIMESTAMP/DATETIME 不存储时区信息。
    • 范围:-4712-01-01 00:00:00.000000000和9999-12-31 23:59:59.999999999
  • DATETIME:同TIMESTAMP。

大对象类型

  • TEXT:为变长字符串类型。其字符串的长度最大为 100G-1 字节。一般存储长的文本串。
  • BLOB:用于指明变长的二进制大对象,长度最大为 100G-1 字节。适用于二进制文件,如图片、PDF、音视频、压缩包等。
  • CLOB:同TEXT。

特殊类型

  • BIT:位类型,用于存储二进制数据(0 或 1),通常用于标志位。
  • ROWID:表中每行数据的物理地址标识符,可视为隐藏的唯一键。常被用于快速定位或删除重复数据。
  • 自增列(IDENTITY):用于生成自动递增的数值列,语法为 IDENTITY(种子, 增量)。种子和增量默认都是1。

DDL语句

表操作类

1. CREATE TABLE

-- 建表语句示例 CREATE TABLE IF NOT EXISTS DM_TEST_EMPLOYEE ( EMP_ID INT NOT NULL, EMP_NAME VARCHAR(100) NOT NULL, SALARY DECIMAL(12, 2) DEFAULT 0, STATUS CHAR(1) DEFAULT '1', CREATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT PK_DM_TEST_EMPLOYEE --主键约束 PRIMARY KEY (EMP_ID), CONSTRAINT UK_DM_TEST_EMP_NAME --唯一约束 UNIQUE (EMP_NAME), CONSTRAINT CK_DM_TEST_EMP_SALARY --检查约束,工资必须大于等于0 CHECK (SALARY >= 0), CONSTRAINT CK_DM_TEST_EMP_STATUS --检查约束,状态只能为0或1 CHECK (STATUS IN ('0', '1')) ) STORAGE --存储参数 ( INITIAL 1, --初始分配一个簇 NEXT 1, --当分配空间不足时再分配一个簇 MINEXTENTS 1, --至少保留一个簇 FILLFACTOR 80, --表示数据插入页时,目标填充比例为 80%,预留约 20% 的页内空间供后续更新使用 ON MAIN --表存放在 MIAN 表空间中 CLUSTERBTR --使用索引组织表(默认),堆表(NOBRANCH)一般不建议使用,性能方面远远不如索引组织表。 );

约束定义
约束用于保证表中数据的完整性,可以写成列级约束或表级约束。列级约束在列的定义之后;表级约束在所有列定义之后,用逗号单独分离。

  • 非空约束:NOT NULL,表示字段不能为空。
  • 默认值:DEFAULT,插入数据时如果不指定目标列,则将目标列设为默认值。
  • 主键约束:PRIMARY KEY,定义主键,一张表只能有一个,不允许空值。
  • 唯一约束:UNIQUE,保证目标列的非空值不能重复,可以有多个,允许空值。
  • 检查约束:CHECK,对目标值进行检查。
  • 外键约束:FOREIGN KEY,与其他表进行关联。

存储参数

  • INITIAL
    • 表示建立表时初始分配的簇数。
    • 单位是簇
    • 普通表允许值通常为 1~256;
    • 默认值通常为 1。
  • NEXT
    • 表示原有空间不足时,下次再分配的簇数。
    • 数值较小:空间利用较细,但扩展可能更频繁;
    • 数值较大:减少扩展次数,但可能预占较多空间。
  • MINEXTENTS
    • 表示删除数据并回收空间后,至少为该表保留的簇数。
    • 它不是最大空间限制,表仍然可以继续扩展。
  • FILLFACTOR
    • 表示数据页的目标填充比例。
    • 适用于频繁更新、记录可能变长的表,可以降低页面分裂概率。代价是表可能占用更多页面。
    • 取值范围为 0~100;在 DM 中,0 等价于 100,表示全满填充。
  • ON
    • 表示将表存放到对应表空间。

表空间指定
两种方法不能同时使用。

  • ON:见上。
  • TABLESPACE + 表空间,放到括号之后即可。

注释添加
建表后通过 COMMENT ON添加。如果已经有注释了再添加注释会覆盖。

COMMENT ON TABLE DM_TEST_EMPLOYEE IS '员工基本信息表'; COMMENT ON COLUMN DM_TEST_EMPLOYEE.EMP_ID IS '员工唯一编号';

临时表
DM8 支持全局临时表。全局临时表具有以下特点:

  1. 表结构长期保存在数据库中;
  2. 表中的数据是临时的;
  3. 不同会话的数据相互隔离;
  4. 一个会话看不到另一个会话写入的数据;
  5. 临时表也可以建立索引和约束;
  6. 临时数据通常产生较少的 REDO 日志。

事务级临时表

CREATE GLOBAL TEMPORARY TABLE DM_TMP_EMPLOYEE ( EMP_ID INT, EMP_NAME VARCHAR(100), AMOUNT DECIMAL(12,2) ) ON COMMIT DELETE ROWS;

ON COMMIT DELETE ROWS 表示:

  • 当前事务可以正常插入、修改和查询数据;
  • 执行 COMMIT 后清空数据;
  • 执行 ROLLBACK 后也会清空相应临时数据;
  • 表结构不会删除。

提交事务之前,可以查询到数据。
alt text

提交事务之后,数据被清除。
alt text

会话级临时表

CREATE GLOBAL TEMPORARY TABLE DM_TMP_EMPLOYEE_SESSION ( EMP_ID INT, EMP_NAME VARCHAR(100), AMOUNT DECIMAL(12,2) ) ON COMMIT PRESERVE ROWS;

ON COMMIT PRESERVE ROWS 表示:

  • 执行 COMMIT 后仍保留数据;
  • 当前会话断开后清空数据;
  • 表结构仍保存在数据库中。

提交之后同一会话可以查询到数据
alt text

不同会话无法查询到数据
alt text

2. ALTER TABLE

ALTER TABLE [模式名.]表名 修改操作;

字段增删改

增加字段:

ALTER TABLE EMPLOYEE ADD COLUMN PHONE VARCHAR(30);

一次增加多个字段:

ALTER TABLE EMPLOYEE ADD (ADDRESS VARCHAR(200), AGE INT);

删除字段:

ALTER TABLE EMPLOYEE DROP COLUMN PHONE;

级联删除相关依赖:

ALTER TABLE EMPLOYEE DROP COLUMN PHONE CASCADE;

修改字段类型或长度:

ALTER TABLE EMPLOYEE MODIFY EMP_NAME VARCHAR(200);

设置或删除默认值:

ALTER TABLE EMPLOYEE ALTER COLUMN STATUS SET DEFAULT '1'; ALTER TABLE EMPLOYEE ALTER COLUMN STATUS DROP DEFAULT;

设置是否允许空值:

ALTER TABLE EMPLOYEE ALTER COLUMN STATUS SET NOT NULL; ALTER TABLE EMPLOYEE ALTER COLUMN STATUS SET NULL;

可能的影响:

  • 增加字段:已有行的新字段为 NULL 或默认值。
  • 删除字段:该字段的数据永久丢失,相关对象可能失效。
  • 修改类型:已有数据不能转换时操作失败。
  • 设置 NOT NULL:已有数据存在 NULL 时操作失败。
  • 修改默认值:只影响以后插入的数据。

约束增删

增加主键:

ALTER TABLE EMPLOYEE ADD CONSTRAINT PK_EMPLOYEE PRIMARY KEY (EMP_ID);

增加唯一约束:

ALTER TABLE EMPLOYEE ADD CONSTRAINT UK_EMPLOYEE_NO UNIQUE (EMP_NO);

增加外键:

ALTER TABLE EMPLOYEE ADD CONSTRAINT FK_EMPLOYEE_DEPT FOREIGN KEY (DEPT_ID) REFERENCES DEPARTMENT(DEPT_ID);

增加检查约束:

ALTER TABLE EMPLOYEE ADD CONSTRAINT CK_EMPLOYEE_SALARY CHECK (SALARY >= 0);

删除约束:

ALTER TABLE EMPLOYEE DROP CONSTRAINT FK_EMPLOYEE_DEPT;

删除主键:

ALTER TABLE EMPLOYEE DROP PRIMARY KEY;

可能的影响:

  • 增加约束时会检查已有数据,不符合规则则操作失败。
  • 删除约束不会删除现有数据,但后续数据不再受该规则限制。
  • CASCADE 可能同时删除相关引用约束。

表名、字段名重命名

修改表名:

ALTER TABLE EMPLOYEE RENAME TO EMPLOYEE_INFO;

修改字段名:

ALTER TABLE EMPLOYEE_INFO RENAME COLUMN EMP_NAME TO EMPLOYEE_NAME;

修改约束名:

ALTER TABLE EMPLOYEE_INFO RENAME CONSTRAINT UK_EMPLOYEE_NO TO UK_EMP_NO;

影响:

  • 数据本身不变。
  • 使用旧名称的 SQL、视图、存储过程或应用程序代码可能失效。

表属性修改

移动表空间:

ALTER TABLE EMPLOYEE MOVE TABLESPACE BUSINESS_TS;

设置并行度:

--数据库对这张表执行全表扫描、创建索引、数据加载等操作时,会被允许最多使用 4 个工作线程来并行处理。 --需要开启PARALLEL_POLICY 参数来生效(默认0关闭;1并行,配置生效;2手动HINT模式) ALTER TABLE EMPLOYEE PARALLEL 4; ALTER TABLE EMPLOYEE NOPARALLEL;

设置只读或读写:

ALTER TABLE EMPLOYEE READ ONLY; ALTER TABLE EMPLOYEE READ WRITE;

启用或禁用触发器:

ALTER TABLE EMPLOYEE ENABLE ALL TRIGGERS; ALTER TABLE EMPLOYEE DISABLE ALL TRIGGERS;

可能的影响:

  • 移动表空间:数据内容不变,但可能长时间锁表,ROWID 可能改变。
  • 修改并行度:数据不变,但会影响执行性能和资源使用。
  • 设置只读:禁止修改表中数据。
  • 禁用触发器:期间执行的增删改不会触发相关逻辑。

3.DROP TABLE
标准删除语法

DROP TABLE [IF EXISTS] [模式名.]表名 [RESTRICT | CASCADE];

示例:

DROP TABLE EMPLOYEE;

表不存在时不报错:

DROP TABLE IF EXISTS EMPLOYEE;

限制删除:

DROP TABLE EMPLOYEE RESTRICT;

级联处理依赖:

DROP TABLE EMPLOYEE CASCADE;

说明:

  • RESTRICT:默认方式。存在视图或外键依赖时拒绝删除。
  • CASCADE:删除相关引用约束;是否删除依赖视图还受 DROP_CASCADE_VIEW 参数影响。
  • 删除表时,表数据、索引和对象权限也会被删除。

PURGE

回收站清理,默认不开启,需要开启 RECYCLEBIN,为0时关闭,1仅DROP进入回收站,2 DROP和TRUNCATE都会进入回收站。 (静态参数,需要重启数据库生效)

DROP TABLE EMPLOYEE; PURGE TABLE EMPLOYEE;

清空当前用户回收站:

PURGE RECYCLEBIN;

DBA 清空数据库回收站:

PURGE DBA_RECYCLEBIN;

作用:

  • 彻底清除回收站中的表;
  • 释放其占用空间;
  • 清除后不能再通过闪回恢复。

适用场景:

  • 确认表不再需要;
  • 测试表、临时业务表需要彻底清理;
  • 回收站占用空间过多。

如果存在误删风险,不建议立即 PURGE。在回收站功能开启时,可以尝试以下命令来恢复被DROP的表。

FLASHBACK TABLE EMPLOYEE TO BEFORE DROP;

被依赖对象的处理策略

视图

推荐先查看并保存视图定义,再显式处理:

DROP VIEW V_EMPLOYEE; DROP TABLE EMPLOYEE RESTRICT;

也可以使用:

DROP TABLE EMPLOYEE CASCADE;

但依赖视图是否被自动删除受 DROP_CASCADE_VIEW 参数影响,因此生产环境更推荐显式删除和重建视图。

外键

建议先删除外键约束:

ALTER TABLE ORDER_INFO DROP CONSTRAINT FK_ORDER_EMPLOYEE; DROP TABLE EMPLOYEE RESTRICT;

使用 CASCADE 时,通常删除的是引用约束,而不是直接删除子表及其数据。

存储过程、函数和触发器

ALTER PROCEDURE P_EMPLOYEE_QUERY COMPILE; ALTER FUNCTION F_EMPLOYEE_COUNT COMPILE; ALTER TRIGGER TRG_EMPLOYEE COMPILE;

删除表后,引用该表的程序对象可能失效或在执行时报错。建议:

  1. 删除前检查并保存依赖对象定义;
  2. 删除或重建表;
  3. 修改相关 SQL;
  4. 重新编译并测试依赖对象。

动态 SQL 中的表名可能无法通过数据库依赖关系自动发现,还需要检查应用程序代码。

TRUNCATE TABLE
快速、高效地清空一个表的数据。通过直接释放整个表的数据页,不可回滚,保留结构。
可以闪回,但是需要将RECYCLEBIN 的值设为2(1无法将其放到回收站里)。

特性维度 TRUNCATE TABLE (DDL) DELETE (DML)
命令类型 数据定义语言(DDL) 数据操纵语言(DML)
事务处理 无法回滚。执行时会隐式提交事务,一旦执行,数据永久删除。可通过闪回技术(需开启回收站)恢复。 支持回滚。在事务中可以执行ROLLBACK恢复数据。
执行原理 释放整个表的数据页,不逐行操作,所以极快。 逐行删除,并记录详细日志用于回滚,所以速度较慢。
触发器响应 不触发ON DELETE触发器。 会触发ON DELETE触发器。
空间回收 立即回收表空间,表空间使用率会下降。但操作系统层面的磁盘文件大小不会自动缩小。 不立即回收。被删行的空间标记为可复用,但表空间使用率不会立刻下降。实际物理释放受UNDO_RETENTION参数影响,需等待回滚段清理。
执行速度 非常快,尤其适合清空大表。 慢,数据量大时会产生大量日志,效率低。

索引创建类

CREATE INDEX

普通 B 树索引

CREATE INDEX hr.idx_emp_name ON hr.emp (ename) TABLESPACE idx_space; -- 指定存储场景

适用场景:

  • 大表上的等值查询;
  • 范围查询,如 >、<、BETWEEN;
  • 排序、连接条件;
  • 经常作为 WHERE、JOIN、ORDER BY 条件的列。

不适合:

  • 数据量很小的表;
  • 几乎不参与查询的列;
  • 区分度很低且查询过滤效果很差的列。

唯一索引

CREATE UNIQUE INDEX idx_emp_no ON emp (emp_no);

唯一索引要求索引键值不能重复,可以同时提高查询性能并保证数据唯一性。

适用场景:

  • 员工编号、身份证号、订单号等业务唯一字段;
  • 需要保证多个字段组合唯一的场景。

组合唯一索引:

CREATE UNIQUE INDEX idx_order_customer ON orders (customer_id, order_no);

说明:主键约束和唯一约束通常会自动创建相应的唯一索引,因此不应重复创建功能相同的索引。

位图索引

CREATE BITMAP INDEX idx_emp_gender ON emp (gender);

位图索引适合取值数量少、重复值多的列,例如:

  • 性别;
  • 是否有效;
  • 状态;
  • 地区等级;
  • 产品类别;
  • 数据仓库中的维度字段。

不适合:

  • 高频 INSERT、UPDATE、DELETE 的 OLTP 表;
  • 高并发写入场景;
  • 取值种类很多的列。

函数索引

函数索引可以建立在函数或表达式上:

CREATE INDEX idx_emp_upper_name ON emp (UPPER(ename));

查询时应尽量使用与索引表达式一致的写法:

SELECT * FROM emp WHERE UPPER(ename) = 'ZHANGSAN';

表达式索引示例:

CREATE INDEX idx_order_amount ON orders (quantity * price);

对应查询:

SELECT * FROM orders WHERE quantity * price > 1000;

适用场景:

  • 查询条件中经常对列执行函数;
  • 需要忽略大小写查询;
  • 对多个列进行固定表达式计算;
  • 避免查询时重复计算。

注意事项:

  • 自定义函数应具有确定性;
  • 不应使用 SYSDATE、RAND 等每次结果可能不同的函数;
  • 函数索引表达式应与查询条件保持一致;
  • 函数定义发生变化后,通常需要重建函数索引;
  • 函数索引会增加数据写入时的计算和维护成本。

组合索引

组合索引也称复合索引,在多个列上建立一个索引:

CREATE INDEX idx_order_customer_date ON orders (customer_id, order_date);

也可以指定排序方向:

CREATE INDEX idx_order_status_date ON orders (status ASC, order_date DESC);

适用场景:

  • 查询经常同时使用多个条件列;
  • 多列组合可以显著减少返回行数;
  • 需要同时支持过滤和排序。

组合索引本质上仍是 B+ 树索引,只是索引键由多个列组成。

全文索引
全文检索专门用于处理非结构化文本数据,通过建立倒排索引实现高效的关键词定位。

CREATE CONTEXT INDEX cti_article_content ON articles (content) LEXER CHINESE_LEXER;

指定创建后立即进行全文索引填充:

CREATE CONTEXT INDEX cti_article_content ON articles (content) LEXER CHINESE_LEXER SYNC;

提交事务后自动增量更新:

CREATE CONTEXT INDEX cti_article_content ON articles (content) LEXER CHINESE_LEXER SYNC TRANSACTION;

支持的分词器包括:

CHINESE_LEXER --中文最小分词 : CHINESE_VGRAM_LEXER --机械双字分词 CHINESE_FP_LEXER --中文最多分词 ENGLISH_LEXER --英文分词 DEFAULT_LEXER --默认分词(中文最小分词)

alt text
全文检索示例:

SELECT * FROM articles WHERE CONTAINS(content, '数据库');

适用场景:

  • 文章正文;
  • 新闻内容;
  • 商品描述;
  • 简介、评论、文档等文本字段。

全文索引支持的列类型包括 CHAR、VARCHAR、VARCHAR2、LONGVARCHAR、TEXT 和 CLOB。全文索引创建后需要进行完全更新或增量更新,填充完成后才能正常使用。
alt text

索引维护

查看索引信息

可以查询用户索引视图:

SELECT TABLE_NAME, INDEX_NAME, STATUS, VISIBILITY FROM SYS.USER_INDEXES;

查看索引定义:

SELECT INDEXDEF(index_id, 1);

其中 INDEXDEF 的第一个参数是索引 ID,可从系统视图中获得。


重建索引

ALTER INDEX idx_emp_name REBUILD;

在线重建:

ALTER INDEX idx_emp_name REBUILD ONLINE;

重建索引常用于:

  • 索引被设置为 UNUSABLE 后恢复;
  • 减少索引碎片;
  • 大量数据变更后整理索引结构;
  • 函数或确定性表达式发生变化后更新索引。

生产环境重建时应结合锁等待、并发访问和维护窗口选择普通重建或在线重建。

禁用索引

ALTER INDEX idx_emp_name UNUSABLE;

设置为 UNUSABLE 后:

  • 优化器不会选择该索引;
  • DML 通常不再维护该索引;
  • 查询不能依赖该索引;
  • 后续需要通过 REBUILD 恢复。

恢复索引:

ALTER INDEX idx_emp_name REBUILD;

隐藏和显示索引

如果只是希望测试“不使用某个索引”时的执行计划,建议使用 INVISIBLE,而不是 UNUSABLE:

ALTER INDEX idx_emp_name INVISIBLE;

恢复可见:

ALTER INDEX idx_emp_name VISIBLE;

二者区别:

操作 执行计划 DML 是否维护索引
UNUSABLE 不使用 通常不维护
INVISIBLE 默认不使用 仍然维护

因此:

  • 批量装载、批量更新期间,可以考虑 UNUSABLE,但必须评估约束和业务影响;
  • 测试索引是否有用,优先使用 INVISIBLE;
  • INVISIBLE 不等于删除索引。

索引使用监控

开启索引使用监控:

ALTER INDEX idx_emp_name MONITORING USAGE;

关闭监控:

ALTER INDEX idx_emp_name NOMONITORING USAGE;

查看监控信息:

SELECT * FROM SYS.V$OBJECT_USAGE;

可以根据业务高峰期的监控结果,识别长期未使用的索引,再结合 SQL、执行计划和业务需求决定是否删除。


删除普通索引

DROP INDEX idx_emp_name;

删除前应确认:

  • 索引不是主键或唯一约束依赖的索引;
  • 没有重要 SQL 依赖该索引;
  • 已通过监控和执行计划确认索引冗余;
  • 已评估删除后的查询性能。

全文索引使用专用删除语法:

DROP CONTEXT INDEX cti_article_content;

索引使用规范

组合索引的最左前缀原则

假设创建索引:

CREATE INDEX idx_a_b_c ON t (a, b, c);

该索引的有效前缀依次为:

(a)
(a, b)
(a, b, c)

通常可以有效支持:

WHERE a = 1; WHERE a = 1 AND b = 2; WHERE a = 1 AND b = 2 AND c = 3; WHERE a = 1 AND b > 2;

通常不能充分利用该索引的情况:

WHERE b = 2; WHERE c = 3; WHERE b = 2 AND c = 3;

设计原则:

  • 等值过滤列通常放在前面;
  • 范围过滤列通常放在等值列之后;
  • 经常参与排序、分组的列可结合实际执行计划放置;
  • 不要仅按列区分度排序,应结合真实 SQL;
  • 对组合索引而言,列顺序通常比列数量更重要。

索引选择性与性能

索引选择性可简单理解为:

选择性 = 不同值数量 ÷ 总行数

选择性越高,索引过滤后的结果集越小,索引通常越有价值。

示例:

列 数据特点 选择性 通常适合
员工编号 基本不重复 高 B 树、唯一索引
手机号 基本不重复 高 B 树、唯一索引
性别 只有少数几种值 低 位图索引或不建普通索引
是否删除 只有 0/1 很低 位图索引、组合索引中的辅助列
状态 几种固定状态 较低 视查询比例选择 B 树或位图

但选择性并不是唯一标准:

  • 低选择性列如果查询只返回极少数数据,仍可能适合建立索引;
  • 高选择性列如果表很小,索引可能没有收益;
  • 优化器会根据统计信息和访问成本决定是否使用索引;
  • 返回表中大部分数据时,全表扫描可能比索引扫描更快。

因此应结合:

EXPLAIN SELECT ... FROM ...;

以及实际执行耗时、返回行数和统计信息综合判断。

常用操作

-- 普通 B 树索引 CREATE INDEX idx_t_a ON t(a); -- 唯一索引 CREATE UNIQUE INDEX idx_t_code ON t(code); -- 位图索引 CREATE BITMAP INDEX idx_t_status ON t(status); -- 函数索引 CREATE INDEX idx_t_upper_name ON t(UPPER(name)); -- 组合索引 CREATE INDEX idx_t_a_b_c ON t(a, b, c); -- 全文索引 CREATE CONTEXT INDEX cti_t_content ON t(content) LEXER CHINESE_LEXER SYNC; -- 重建索引 ALTER INDEX idx_t_a REBUILD; -- 在线重建 ALTER INDEX idx_t_a REBUILD ONLINE; -- 设置索引无效 ALTER INDEX idx_t_a UNUSABLE; -- 恢复索引有效 ALTER INDEX idx_t_a REBUILD; -- 隐藏索引 ALTER INDEX idx_t_a INVISIBLE; -- 恢复可见 ALTER INDEX idx_t_a VISIBLE; -- 开启使用监控 ALTER INDEX idx_t_a MONITORING USAGE; -- 删除普通索引 DROP INDEX idx_t_a; -- 删除全文索引 DROP CONTEXT INDEX cti_t_content;

视图操作类

CREATE VIEW:普通视图与物化视图

普通视图只保存查询定义,不保存数据;查询时实时访问基表。物化视图保存查询结果,可通过刷新保持与基表同步,适合报表和汇总查询。

-- 普通视图 CREATE [OR REPLACE] VIEW [模式名.]V_EMPLOYEE AS SELECT EMP_ID, EMP_NAME, DEPT_ID, SALARY FROM EMPLOYEE WHERE STATUS = '1'; -- 物化视图(DM 版本支持的语法以实际版本手册为准) CREATE MATERIALIZED VIEW [模式名.]MV_DEPT_SALARY BUILD IMMEDIATE REFRESH COMPLETE ON DEMAND AS SELECT DEPT_ID, SUM(SALARY) AS TOTAL_SALARY FROM EMPLOYEE GROUP BY DEPT_ID;

普通视图适合统一查询接口、字段屏蔽和权限隔离;物化视图适合复杂聚合、跨表报表和读多写少场景。物化视图需要额外存储空间,并要设计刷新时机;BUILD IMMEDIATE 创建时立即生成数据,ON DEMAND 表示由人工或任务触发刷新。

ALTER VIEW / DROP VIEW

-- 编译视图 ALTER VIEW V_EMPLOYEE COMPILE; -- 删除视图 DROP VIEW [IF EXISTS] [模式名.]V_EMPLOYEE [CASCADE];

视图查询定义通常通过 CREATE OR REPLACE VIEW 修改;ALTER VIEW 主要用于编译、属性维护等操作。删除视图不会删除基表数据;使用 CASCADE 前应确认依赖对象会受到影响。

WITH CHECK OPTION / WITH READ ONLY

CREATE VIEW V_ACTIVE_EMP AS SELECT EMP_ID, EMP_NAME, STATUS FROM EMPLOYEE WHERE STATUS = '1' WITH CHECK OPTION; CREATE VIEW V_EMP_READONLY AS SELECT EMP_ID, EMP_NAME, SALARY FROM EMPLOYEE WITH READ ONLY;

WITH CHECK OPTION 要求通过视图执行 INSERT 或 UPDATE 后,数据仍满足视图的 WHERE 条件,防止更新后数据“跑出”视图;WITH READ ONLY 禁止通过该视图执行增删改。复杂连接、聚合、DISTINCT 等视图即使未显式指定只读,也可能天然不可更新。

其他 DDL 语句

CREATE SCHEMA:模式创建与授权

CREATE SCHEMA AUTHORIZATION APP_USER; -- 在模式中创建对象并授权 CREATE SCHEMA AUTHORIZATION APP_USER CREATE TABLE T_ORDER ( ORDER_ID INT PRIMARY KEY, AMOUNT DECIMAL(12,2) ) GRANT SELECT ON T_ORDER TO REPORT_USER;

模式通常与数据库用户关联,用于对象命名空间和权限隔离。实际环境中常先由 DBA 创建用户,再由对象所有者或 DBA 执行 GRANT;不要把模式名、用户名和表名混用。

CREATE SEQUENCE:序列参数

CREATE SEQUENCE SEQ_ORDER_ID INCREMENT BY 1 START WITH 1000 MAXVALUE 999999999 NOCYCLE CACHE 20; SELECT SEQ_ORDER_ID.NEXTVAL FROM DUAL; SELECT SEQ_ORDER_ID.CURRVAL FROM DUAL;

INCREMENT BY 指定步长;START WITH 指定初始值;MAXVALUE/MINVALUE 限定边界;CYCLE 到边界后循环,NOCYCLE 到边界时报错;CACHE n 预缓存序列值以减少争用,数据库异常中止可能造成间隔跳号。序列不保证连续,也不等同于无间隙业务编号。

CREATE SYNONYM:同义词

CREATE [OR REPLACE] SYNONYM S_EMPLOYEE FOR HR.EMPLOYEE; CREATE PUBLIC SYNONYM S_EMPLOYEE FOR HR.EMPLOYEE;

私有同义词只对创建者(或被授权用户)可见;公共同义词对所有用户可见,但仍需具备目标对象权限。适合屏蔽模式名、兼容对象改名和简化跨模式访问;生产环境应谨慎使用公共同义词,避免命名冲突和权限边界不清。

COMMENT:注释规范

COMMENT ON TABLE EMPLOYEE IS '员工基本信息'; COMMENT ON COLUMN EMPLOYEE.EMP_ID IS '员工唯一编号'; COMMENT ON VIEW V_ACTIVE_EMP IS '在职员工查询视图';

注释对象名应写明模式名(跨模式时使用 模式.对象),文本使用单引号;再次执行会覆盖原注释,删除注释可使用 IS NULL(以当前 DM 版本支持情况为准)。

DML 语句

INSERT:单行、多行与查询插入

-- 单行插入:适合少量、参数化写入 INSERT INTO EMPLOYEE (EMP_ID, EMP_NAME, DEPT_ID) VALUES (1001, '张三', 10); -- 多行插入:一次语句写入多组常量 INSERT ALL INTO EMPLOYEE (EMP_ID, EMP_NAME, DEPT_ID) VALUES (1002, '李四', 10) INTO EMPLOYEE (EMP_ID, EMP_NAME, DEPT_ID) VALUES (1003, '王五', 20) SELECT 1 FROM DUAL; -- 从查询结果插入:适合装载、迁移和汇总结果 INSERT INTO EMPLOYEE_HIS (EMP_ID, EMP_NAME, DEPT_ID) SELECT EMP_ID, EMP_NAME, DEPT_ID FROM EMPLOYEE WHERE STATUS = '0';

单行写法可读性和参数绑定最好;INSERT ALL 减少往返次数;INSERT ... SELECT 通常最适合批量装载,但应关注日志、锁、约束和目标表索引维护成本。

UPDATE:标准与关联更新

UPDATE EMPLOYEE SET SALARY = SALARY * 1.05, UPDATE_TIME = CURRENT_TIMESTAMP WHERE DEPT_ID = 10; -- DM 常用相关子查询写法(兼容性较好) UPDATE EMPLOYEE E SET E.DEPT_NAME = (SELECT D.DEPT_NAME FROM DEPARTMENT D WHERE D.DEPT_ID = E.DEPT_ID) WHERE EXISTS (SELECT 1 FROM DEPARTMENT D WHERE D.DEPT_ID = E.DEPT_ID);

关联更新的连接键应保证一对一;若子查询返回多行会报错。更新前先用同一 WHERE 条件执行 SELECT 核对影响范围,并注意触发器、唯一约束和空值传播。若目标 DM 版本启用 UPDATE ... FROM 扩展,可写为:

UPDATE EMPLOYEE E SET E.SALARY = E.SALARY * 1.05 FROM DEPARTMENT D WHERE D.DEPT_ID = E.DEPT_ID AND D.DEPT_LEVEL = 'A';

不同兼容模式对该扩展的支持可能不同;跨版本脚本优先使用前面的相关子查询写法并实测。

DELETE:标准与关联删除

DELETE FROM EMPLOYEE WHERE STATUS = '0'; -- 关联删除的通用写法 DELETE FROM EMPLOYEE E WHERE EXISTS ( SELECT 1 FROM DEPARTMENT D WHERE D.DEPT_ID = E.DEPT_ID AND D.ENABLED = '0' );

关联删除应使用 EXISTS 或主键集合限定目标行,避免连接产生重复匹配。若目标 DM 版本支持 DELETE ... FROM 扩展,可写为:

DELETE FROM EMPLOYEE E USING DEPARTMENT D WHERE D.DEPT_ID = E.DEPT_ID AND D.ENABLED = '0';

执行前确认外键、级联规则和触发器;大批量删除可分批提交,并评估日志与锁。DELETE ... FROM/USING 不是所有 DM 版本都兼容,跨版本脚本应优先采用 EXISTS 写法并按目标版本验证。

MERGE:同步与加载

MERGE INTO EMPLOYEE T USING EMPLOYEE_STAGE S ON (T.EMP_ID = S.EMP_ID) WHEN MATCHED THEN UPDATE SET T.EMP_NAME = S.EMP_NAME, T.SALARY = S.SALARY WHEN NOT MATCHED THEN INSERT (EMP_ID, EMP_NAME, SALARY) VALUES (S.EMP_ID, S.EMP_NAME, S.SALARY);

结构可概括为:MERGE INTO 目标 USING 来源 ON 匹配条件 → WHEN MATCHED(更新/删除) → WHEN NOT MATCHED(插入)。WHEN MATCHED 处理已存在键,WHEN NOT MATCHED 处理新增键;来源键必须唯一,否则同一目标行可能被多次匹配。适用于增量同步、ETL 装载和维表合并,执行前应校验来源数据去重及唯一约束。

RETURNING 子句

INSERT INTO EMPLOYEE (EMP_ID, EMP_NAME) VALUES (SEQ_EMP_ID.NEXTVAL, '赵六') RETURNING EMP_ID INTO :new_emp_id; UPDATE EMPLOYEE SET SALARY = SALARY + 1000 WHERE EMP_ID = :emp_id RETURNING EMP_ID, SALARY INTO :out_id, :out_salary;

RETURNING 在 DML 完成后直接返回受影响行的列值,适合获取自增/序列主键、更新后的金额或删除前的业务字段,可减少再次查询。批量返回、客户端绑定变量和驱动支持存在差异,应按使用的 DM 驱动验证。

DQL 查询语句

SELECT 完整结构与执行顺序

SELECT [DISTINCT] 列或表达式 FROM 表或视图 [JOIN ... ON ...] [WHERE 条件] [GROUP BY 分组列] [HAVING 分组条件] [ORDER BY 排序列 [ASC|DESC] [NULLS FIRST|LAST]] [LIMIT n [OFFSET m]];

逻辑执行顺序通常为:FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT。窗口函数在 SELECT 阶段计算,不能直接写在 WHERE 中;需要先用子查询或公用表表达式筛选。

WHERE 子句

WHERE SALARY >= 5000 AND DEPT_ID IN (10, 20) AND HIRE_DATE BETWEEN DATE '2020-01-01' AND DATE '2024-12-31' AND EMP_NAME LIKE '张%' AND PHONE IS NULL

比较运算符:=、<>、!=、>、>=、<、<=;逻辑运算符:NOT、AND、OR,混用时用括号明确优先级。BETWEEN 两端包含边界;IN 遇到 NULL 时要结合三值逻辑;LIKE 中 % 匹配任意长度、_ 匹配单个字符;判断空值必须使用 IS NULL/IS NOT NULL,不能写 = NULL。

ORDER BY 与空值顺序

SELECT EMP_ID, SALARY, HIRE_DATE FROM EMPLOYEE ORDER BY DEPT_ID ASC, SALARY DESC NULLS LAST, EMP_ID;

可按多列排序,前列相同时再比较后列。为保证分页稳定,应追加唯一键;NULLS FIRST/LAST 显式控制空值位置,避免受数据库默认排序规则影响。

LIMIT / TOP / ROWNUM 分页

-- LIMIT:适合支持 LIMIT 的版本 SELECT * FROM EMPLOYEE ORDER BY EMP_ID LIMIT 20 OFFSET 40; -- TOP:取前 N 行 SELECT TOP 20 * FROM EMPLOYEE ORDER BY EMP_ID; -- ROWNUM:用嵌套查询实现区间分页 SELECT * FROM ( SELECT E.*, ROWNUM RN FROM (SELECT * FROM EMPLOYEE ORDER BY EMP_ID) E WHERE ROWNUM <= 60 ) WHERE RN > 40;

LIMIT 直观且适合现代分页;TOP 适合只取前几行;ROWNUM 兼容传统 Oracle 风格但必须先排序再截取。大偏移量分页会扫描并丢弃前面大量行,海量数据建议使用基于唯一键的“游标/键集分页”。

GROUP BY 与多维分组

SELECT DEPT_ID, STATUS, COUNT(*) AS CNT, SUM(SALARY) AS TOTAL FROM EMPLOYEE GROUP BY DEPT_ID, STATUS; SELECT DEPT_ID, STATUS, SUM(SALARY) FROM EMPLOYEE GROUP BY ROLLUP (DEPT_ID, STATUS); SELECT DEPT_ID, STATUS, SUM(SALARY) FROM EMPLOYEE GROUP BY CUBE (DEPT_ID, STATUS); SELECT DEPT_ID, STATUS, SUM(SALARY) FROM EMPLOYEE GROUP BY GROUPING SETS ((DEPT_ID), (STATUS), ());

ROLLUP 生成从明细到逐级小计及总计;CUBE 生成所有维度组合;GROUPING SETS 精确指定需要的分组集合,避免不必要的组合。小计行中的 NULL 可结合 GROUPING() 区分真实空值与汇总空值。

聚合函数

函数 签名示例 NULL 与 DISTINCT 注意
COUNT COUNT(*)、COUNT(expr)、COUNT(DISTINCT expr) COUNT(*) 统计行数;COUNT(expr) 忽略 NULL;DISTINCT 统计去重非空值。
SUM SUM([DISTINCT] expr) 忽略 NULL;全为 NULL 时结果为 NULL。
AVG AVG([DISTINCT] expr) 忽略 NULL,按非空值计算平均数。
MAX/MIN MAX(expr)、MIN(expr) 忽略 NULL,可用于日期和字符比较。
LISTAGG LISTAGG(expr, ',') WITHIN GROUP (ORDER BY expr) 忽略 NULL;需控制结果长度,排序决定拼接顺序。
WM_CONCAT WM_CONCAT(expr) 达梦兼容函数,版本/兼容模式可能有差异;新开发优先使用 LISTAGG。

HAVING:分组后过滤

SELECT DEPT_ID, COUNT(*) AS CNT FROM EMPLOYEE WHERE STATUS = '1' GROUP BY DEPT_ID HAVING COUNT(*) >= 10;

WHERE 在分组前过滤明细行,通常更有利于减少参与聚合的数据量;HAVING 在分组后过滤聚合结果,适合 COUNT/SUM/AVG 等条件。能写在 WHERE 的条件不要放到 HAVING。

多表连接

INNER JOIN 与隐式连接

SELECT E.EMP_ID, E.EMP_NAME, D.DEPT_NAME FROM EMPLOYEE E INNER JOIN DEPARTMENT D ON D.DEPT_ID = E.DEPT_ID; -- 隐式连接(不推荐用于复杂 SQL) SELECT E.EMP_ID, E.EMP_NAME, D.DEPT_NAME FROM EMPLOYEE E, DEPARTMENT D WHERE D.DEPT_ID = E.DEPT_ID;

两者在连接条件完整时结果等价;显式 JOIN ... ON 更清晰,也能避免遗漏条件导致笛卡尔积。

LEFT/RIGHT/FULL OUTER JOIN

左连接保留左表全部行,右连接保留右表全部行,全连接保留两侧全部行。连接条件应优先放在 ON 中;把右表过滤条件写入 WHERE 往往会把左连接退化为内连接。

SELECT E.EMP_ID, D.DEPT_NAME FROM EMPLOYEE E LEFT JOIN DEPARTMENT D ON D.DEPT_ID = E.DEPT_ID AND D.ENABLED = '1';

CROSS JOIN

SELECT S.SIZE_NAME, C.COLOR_NAME FROM SIZES S CROSS JOIN COLORS C;

交叉连接返回两表行数的乘积,适合生成商品规格组合、日历与维度展开;使用前应确认数据量,避免意外笛卡尔积。

自连接

SELECT E.EMP_NAME AS EMPLOYEE_NAME, M.EMP_NAME AS MANAGER_NAME FROM EMPLOYEE E LEFT JOIN EMPLOYEE M ON M.EMP_ID = E.MANAGER_ID;

自连接通过同一表的不同别名表示上下级、配对或重复数据比对。树形查询的多层遍历应结合递归查询或层次查询语法,并防止循环引用。

集合操作

SELECT EMP_ID FROM EMPLOYEE_2024 UNION SELECT EMP_ID FROM EMPLOYEE_2025; SELECT EMP_ID FROM EMPLOYEE_2024 UNION ALL SELECT EMP_ID FROM EMPLOYEE_2025; SELECT EMP_ID FROM EMPLOYEE_2024 INTERSECT SELECT EMP_ID FROM EMPLOYEE_2025; SELECT EMP_ID FROM EMPLOYEE_2024 MINUS SELECT EMP_ID FROM EMPLOYEE_2025;

UNION 合并后去重,结果更小但需要排序或哈希,通常慢于 UNION ALL;确认来源集合互不重复时优先 UNION ALL。INTERSECT 返回两边交集;达梦常用 MINUS 求差集,部分兼容模式支持 EXCEPT,迁移时应按数据库模式验证。集合两侧列数、顺序和数据类型必须兼容,最终排序只能写在整个集合查询末尾。

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服