掌握 DM8 常用数据库对象的作用与基本操作
——触发器、视图、物化视图、序列、同义词、自增列与外部链接——
目 录
摘 要 4
1 引言 4
2 触发器 4
2.1 触发器的概念与分类 4
2.2 表触发器:语法与基本元素 4
2.3 触发级别、触发时机与执行顺序 6
2.4 新、旧行值引用 :OLD 与 :NEW 及触发器谓词 8
2.5 事件触发器:DDL 与系统事件 10
2.6 时间触发器 12
2.7 触发器的管理 13
2.8 触发器的应用举例 13
3 视图 14
3.1 视图的概念与作用 14
3.2 视图的定义 15
3.3 视图的查询与编译 16
3.4 视图数据的更新与可更新视图 17
3.5 WITH CHECK OPTION 与 WITH READ ONLY 17
3.6 视图的删除 19
4 物化视图 19
4.1 物化视图的概念 19
4.2 物化视图的定义与分类 20
4.3 刷新方法、刷新时机与刷新规则 21
4.4 物化视图日志 22
4.5 物化视图的主要限制 23
5 序列 24
5.1 序列的概念与定义 24
5.2 CURRVAL 与 NEXTVAL 伪列 25
5.3 序列的修改 26
5.4 序列的删除 27
6 同义词 28
6.1 同义词的概念与作用 28
6.2 创建同义词 28
6.3 同义词的解析顺序 29
6.4 删除同义词 30
7 自增列 31
7.1 自增列的定义 31
7.2 自增列查询函数 32
7.3 SET IDENTITY_INSERT:显式插入自增列值 32
8 外部链接 34
8.1 外部链接的概念 34
8.2 创建外部链接 35
8.3 使用外部链接 36
8.4 使用限制 37
9 最佳实践与总结 38
参考文献 39
触发器、视图、物化视图、序列、同义词、自增列与外部链接是达梦 DM8 中一组极为常用的数据库对象,它们分别承担着自动业务逻辑、逻辑数据独立、预计算加速、主键生成、对象别名、行标识生成与跨库透明访问等职责。本文基于《DM8_SQL 语言使用手册》第 5 章 5.6 节、第 6 章、第 7 章、第 14 章、第 15 章、第 16 章等官方资料,对上述七类对象的概念、作用、定义语法、基本操作与使用限制进行系统梳理,逐一给出可执行的示例代码与要点总结,在掌握基本操作的同时理解各对象的设计意图与适用边界。
关键词:DM8;触发器;视图;物化视图;序列;同义词;自增列;外部链接
一个成熟的生产数据库,除了基表和索引之外,还需要一系列辅助对象来承载业务规则与系统能力:触发器让数据库在 DML/DDL 发生时自动执行逻辑;视图为不同用户提供观察数据的不同窗口;物化视图以空间换时间预存查询结果;序列与自增列为表自动生成唯一递增的标识值;同义词为对象提供位置透明的别名;外部链接则让本地会话可以透明地操作远程数据库。
本报告按照“概念与作用 → 语法与基本操作 → 示例 → 限制与总结”的脉络,逐一对这七类对象展开说明。
触发器(Trigger)是与表、视图或数据库全局对象相关联的、由特定事件自动执行的一段 DMSQL 程序语句块。手册将触发器分为三类:表触发器是对表里数据操作(INSERT/DELETE/UPDATE)引发的数据库的触发;事件触发器是对数据库对象操作(DDL)或系统事件引起的触发;时间触发器是一种特殊的事件触发器,用于定时执行任务。
触发器的显著特点是不能被用户主动“调用”,是否启动由系统根据触发事件决定;触发器不存在执行权限的概念。
表触发器定义语句的语法(摘自《DM8_SQL 语言使用手册》14.1.1.1):
CREATE [OR REPLACE] TRIGGER [<模式名>.]<触发器名> [WITH ENCRYPTION]
<触发限制描述> [REFERENCING <trig_referencing_list>]
[FOR EACH {ROW | STATEMENT}]
[<trigger_ordering_clause>] [WHEN (<条件表达式>)] <触发器体>
<触发限制描述1> ::= <BEFORE|AFTER> <触发事件列表> [LOCAL] ON <触发表名>
<触发限制描述2> ::= INSTEAD OF <触发事件列表> [LOCAL] ON <触发视图名>
<触发事件> ::= INSERT | DELETE | {UPDATE | {UPDATE OF <触发列清单>}}
要点:BEFORE 在执行触发语句之前启动;AFTER 在执行之后启动;INSTEAD OF 替换原始操作,仅允许建立在视图上且只支持元组级触发。触发事件可以是多个数据操作的组合(用 OR 连接),UPDATE 可通过 UPDATE OF <触发列清单> 限定触发列(字段数不超过 128)。FOR EACH ROW 为元组级(行级)触发器,受影响的每条记录执行一次;FOR EACH STATEMENT(可缺省)为语句级触发器,每个触发命令执行一次。WHEN 子句仅允许用于元组级触发器,条件为真才执行。FOLLOWS/PRECEDES 可指定同类型触发器的先后顺序,最多支持 254 个。
示例:在 PERSON.PERSON 表上创建 UPDATE 列级触发器与 INSERT OR DELETE 组合触发器(手册原文):
SET SCHEMA dmoa;
CREATE OR REPLACE TRIGGER TRG_UPD
AFTER UPDATE OF id, name ON DMOA.dmoa
BEGIN
PRINT 'UPDATE OPERATION ON COLUMNS id OR name OF dmoa';
END;
/
CREATE OR REPLACE TRIGGER TRG_INS_DEL
AFTER INSERT OR DELETE ON DMOA.dmoa
BEGIN
PRINT 'INSERT OR DELETE OPERATION ON dmoa';
END;
/
SET SCHEMA SYSDBA;
图2.2-1触发器对修改表数据数据进行打印提示
图2.2-2触发器对修改表数据数据进行打印提示
元组级触发器对触发命令影响的每一条记录都触发一次(例如 DELETE 删除 1000 行则元组级触发器执行 1000 次),常用于数据审计、完整性检查;语句级触发器对每个触发命令只执行一次,常用于为表上的操作类型引入附加安全措施。
执行一条 DML 语句的完整算法步骤(手册原文):①若有语句级前触发器,先运行;②对于受语句影响的每一行:先运行元组级前触发器,再执行语句本身,最后运行元组级后触发器;③若有语句级后触发器,最后运行。
示例验证:在 OTHER.READER 表上同时创建四种 UPDATE 触发器(语句级前、语句级后、元组级前、元组级后),执行 UPDATE OTHER.READER SET AGE=AGE+1 影响 3 行时,语句级前后触发器各运行 1 次,元组级前后触发器各运行 3 次。
SELECT * from DMOA a;
-- 在 OTHER.EMPLOYEE 表上同时创建四种 UPDATE 触发器
CREATE OR REPLACE TRIGGER TRG_STMT_BEFORE
BEFORE UPDATE ON DMOA.DMOA
BEGIN
PRINT '语句级前触发器';
END;
/
CREATE OR REPLACE TRIGGER TRG_STMT_AFTER
AFTER UPDATE ON DMOA.DMOA
BEGIN
PRINT '语句级后触发器';
END;
/
CREATE OR REPLACE TRIGGER TRG_ROW_BEFORE
BEFORE UPDATE ON DMOA.DMOA FOR EACH ROW
BEGIN
PRINT '元组级前触发器';
END;
/
CREATE OR REPLACE TRIGGER TRG_ROW_AFTER
AFTER UPDATE ON DMOA.DMOA FOR EACH ROW
BEGIN
PRINT '元组级后触发器';
END;
/
-- 验证SQL:影响 3 行时:语句级前后各触发 1 次,元组级前后各触发 3 次
UPDATE dmoa set name='宋江' where id='1';
图2.3-1触发次数验证
在一张基表上允许创建的表触发器个数没有限制,合法类型共 12 种,如下表所示:
触发时机 语句级(FOR EACH STATEMENT) 元组级(FOR EACH ROW)
BEFORE INSERT 在一个 INSERT 处理前触发一次 每条新记录插入前触发
AFTER INSERT 在一个 INSERT 处理后触发一次 每条新记录插入后触发
BEFORE DELETE 在一个 DELETE 处理前触发一次 每条记录被删除前触发
AFTER DELETE 在一个 DELETE 处理后触发一次 每条记录被删除后触发
BEFORE UPDATE 在一个 UPDATE 处理前触发一次 每条记录被修改前触发
AFTER UPDATE 在一个 UPDATE 处理后触发一次 每条记录被修改后触发
在元组级触发器内部,可以通过引用变量 :OLD 与 :NEW 访问正在处理记录的数据::OLD 表示记录被处理前的值,:NEW 表示记录被处理后的值。对于 INSERT 操作不存在旧值(:OLD 所有字段为 NULL),对于 DELETE 操作不存在新值(:NEW 所有字段为 NULL)。:OLD 只能读取不能赋值;:NEW 既可读取又可赋值,但只能在 BEFORE 触发器中赋值——通过修改 :NEW 的值可以影响插入或修改的数据。
触发语句 标识符 :OLD 标识符 :NEW
INSERT 无定义,所有字段都为 NULL 该语句结束时将插入的值
UPDATE 更新前行的旧值 该语句结束时将更新的值
DELETE 行删除前的旧值 无定义,所有字段都为 NULL
当触发事件是多个数据操作的组合时,触发器体内可用三个谓词判断当前执行的操作:INSERTING(触发语句为 INSERT 时为真)、DELETING(为 DELETE 时为真)、UPDATING[(<列名>)](未指定列名时为 UPDATE 即为真,指定列名时仅当更新该列时为真)。
--001
-- 例1:BEFORE INSERT 触发器,修改 :NEW 自动填充create_date
-- ======================
CREATE OR REPLACE TRIGGER GenerateValue
BEFORE INSERT ON dmoa.dmoa
FOR EACH ROW
BEGIN
-- 插入前,自动给新行的create_date赋值为当前数据库日期
:new.create_date := SYSDATE;
END;
/
-- ======================
-- 先创建审计表 dmoa_audit,放在dmoa模式下
-- ======================
CREATE TABLE dmoa.dmoa_audit(
ChangeType CHAR(1), -- I新增 U修改 D删除
DB_USER VARCHAR2(50), -- 执行操作的数据库用户
OP_TIME DATE, -- 操作发生时间
old_id INTEGER,
old_name VARCHAR2(50),
old_create_date DATE,
new_id INTEGER,
new_name VARCHAR2(50),
new_create_date DATE
);
/
-- 1. 插入测试:不用写create_date,触发器GenerateValue自动填充日期
INSERT INTO dmoa.dmoa(id,name) VALUES(4,'林冲');
SELECT * from dmoa;
-- 2. 更新测试:修改name,审计触发器记录新旧值
UPDATE dmoa.dmoa SET name='林教头' WHERE id=4;
-- 3. 删除测试:删除这条记录,审计记录旧数据
DELETE FROM dmoa.dmoa WHERE id=4;
-- 查询审计表,查看全部变更记录
SELECT * FROM dmoa.dmoa_audit;
图2.4-1查看变更记录
事件触发器在数据库全局对象上创建,触发事件分为 DDL 事件与系统事件。DDL 事件包括 DDL、CREATE、ALTER、DROP、GRANT、REVOKE、TRUNCATE、COMMENT;系统事件包括 LOGIN/LOGON、LOGOUT/LOGOFF、SERERR、BACKUP DATABASE、RESTORE DATABASE、AUDIT、NOAUDIT、TIMER、STARTUP、SHUTDOWN、CHECKPOINT。触发对象名为 [<模式名>.]SCHEMA 或 DATABASE。
CREATE [OR REPLACE] TRIGGER [<模式名>.]<触发器名> [WITH ENCRYPTION]
<BEFORE|AFTER> <触发事件子句> ON <触发对象名>
[EXECUTE AT <触发的RAFT组名><DMDSC节点号>] [WHEN <条件表达式>] <触发器体>
-- 例1:在 DATABASE 上创建 BEFORE CREATE 事件触发器,记录所有建对象操作
-- 第一步:先在dmoa模式下创建事件审计表 T01_TRI_10000
CREATE TABLE dmoa.T01_TRI_10000(
OBJECTTYPE VARCHAR2(100), -- 对象类型:TABLE/VIEW等
OBJECTNAME VARCHAR2(100), -- 对象名称
SCHEMANAME VARCHAR2(100), -- 对象所属模式
DATABASENAME VARCHAR2(100), -- 数据库名
OPUSER VARCHAR2(100), -- 执行操作的用户
OPTIME DATE -- 操作时间
);
/
-- ==========================
-- 例1:BEFORE CREATE 系统触发器,监听数据库内创建对象事件
-- ==========================
CREATE OR REPLACE TRIGGER TRI01
BEFORE CREATE ON DATABASE
BEGIN
INSERT INTO dmoa.T01_TRI_10000
VALUES (
:EVENTINFO.OBJECTTYPE,
:EVENTINFO.OBJECTNAME,
:EVENTINFO.SCHEMANAME,
:EVENTINFO.DATABASENAME,
:EVENTINFO.OPUSER,
:EVENTINFO.OPTIME
);
END;
/
-- ==========================
-- 例2:AFTER LOGIN 登录触发器,用户登录数据库触发
-- ==========================
CREATE OR REPLACE TRIGGER test_trigger
AFTER LOGIN ON DATABASE
BEGIN
PRINT 'SUCCESS';
END;
/
--进行验证
CREATE TABLE dmoa.test_ddl(id int);
SELECT * FROM dmoa.T01_TRI_10000;
--删除验证数据
DROP TRIGGER TRI01;
DROP TRIGGER test_trigger;
DROP TABLE dmoa.T01_TRI_10000;
图2.5-1查看审计表记录
要点:所有事件信息通过伪变量 :EVENTINFO 取得,例如 :eventinfo.objecttype(对象类型,如 'TABLE'/'INDEX'/'SEQUENCE' 等)、:eventinfo.objectname(对象名称)、:eventinfo.schemaname(所属模式)、:eventinfo.opuser(操作用户名)、:eventinfo.optime(事件时间);GRANT/REVOKE 事件还可取 :eventinfo.granttype 与 :eventinfo.grantee;LOGIN/LOGOUT 事件可取 :eventinfo.loginname;SERERR 事件可取 :eventinfo.errcode 与 :eventinfo.errmsg。事件属性函数 DM_DICT_OBJ_NAME、DM_DICT_OBJ_TYPE、DM_DICT_OBJ_OWNER、DM_SQL_TXT 可获取事件触发时的属性与正在执行的 DDL 语句。
时机限制:所有 DDL 事件触发器都可设置 BEFORE 或 AFTER;系统事件中 LOGOUT 和 SHUTDOWN 仅能设置为 BEFORE,其它只能设置为 AFTER。STARTUP 只能 AFTER,SHUTDOWN 只能 BEFORE,且 SHUTDOWN 触发器中不要执行花费时间多于 5 秒的操作。系统内部 DDL 与 MPP 从节点的 DDL 不触发事件触发器。
时间触发器(AFTER TIMER ON DATABASE)属于特殊的事件触发器,用于定义有规律性执行的、定点执行的任务,例如在夜间服务器负荷轻时更新统计信息、自动备份等,最低时间频率精确到秒级。
触发器的管理包括替换(OR REPLACE,被替换触发器的触发表/触发对象不能改变)、删除、禁止/允许与重编:
DROP TRIGGER [IF EXISTS] [<模式名>.]<触发器名>;
ALTER TRIGGER [<模式名>.]<触发器名> <DISABLE | ENABLE>;
ALTER TRIGGER [<模式名>.]<触发器名> COMPILE [DEBUG];
-- 示例
DROP TRIGGER SYSDBA.TRG2; -- 删除触发器
ALTER TRIGGER OTHER.TRG1 DISABLE; -- 禁止触发器
ALTER TRIGGER OTHER.TRG_AI_ACCOUNT COMPILE; -- 重编译
要点:触发表被删除时表上的触发器被自动删除;载入大量数据时可先 DISABLE 触发器提高速度;触发器体内不允许使用 COMMIT、ROLLBACK 等可能导致事务提交或回滚的语句;水平分区子表与 HUGE 表不支持表触发器;创建触发器要求具有 CREATE TRIGGER 权限或 DBA 角色。
手册给出了四类典型应用:①使用触发器实现审计功能(AFTER 类型,可记录被修改数据的具体值,比内置审计更灵活,但不能取代内置审计——后者能审计失败的操作与更多类型);②使用触发器维护数据完整性(如级联删除、CHECK 规则检查,但能用约束完成的不建议用触发器);③使用触发器保障数据安全性(语句级 BEFORE 类型,在执行前做检查,避免不必要的工作);④使用触发器生成字段默认值(元组级 BEFORE INSERT/UPDATE 触发器)。
-- 应用1:引用完整性维护——删除/更新被引用表时级联维护引用表
-- ========== 1. 创建测试表 dmoa.DEPTTAB(部门表)、dmoa.EMPTAB(员工表) ==========
CREATE TABLE dmoa.DEPTTAB(
Deptno INT PRIMARY KEY,
Dname VARCHAR2(50)
);
/
CREATE TABLE dmoa.EMPTAB(
Empno INT PRIMARY KEY,
Ename VARCHAR2(50),
Deptno INT
);
-- 插入测试数据
INSERT INTO dmoa.DEPTTAB(Deptno,Dname) VALUES(10,'研发部');
INSERT INTO dmoa.DEPTTAB(Deptno,Dname) VALUES(20,'财务部');
INSERT INTO dmoa.EMPTAB(Empno,Ename,Deptno) VALUES(1,'张三',10);
INSERT INTO dmoa.EMPTAB(Empno,Ename,Deptno) VALUES(2,'李四',10);
INSERT INTO dmoa.EMPTAB(Empno,Ename,Deptno) VALUES(3,'王五',20);
COMMIT;
/
-- ========== 应用1:触发器 Dept_del_upd_cascade 级联维护(行级AFTER) ==========
CREATE OR REPLACE TRIGGER Dept_del_upd_cascade
AFTER DELETE OR UPDATE ON dmoa.DEPTTAB
FOR EACH ROW
BEGIN
IF DELETING THEN
-- 删除部门,级联删除该部门下所有员工
DELETE FROM dmoa.EMPTAB WHERE Deptno = :old.Deptno;
ELSE
-- 更新部门编号,员工表部门号同步修改
UPDATE dmoa.EMPTAB SET Deptno = :new.Deptno
WHERE Deptno = :old.Deptno;
END IF;
END;
/
-- ========== 应用2:触发器 Emp_permit_changes 非工作时间安全检查(语句级BEFORE) ==========
CREATE OR REPLACE TRIGGER Emp_permit_changes
BEFORE INSERT OR DELETE OR UPDATE ON dmoa.EMPTAB
DECLARE
Dummy INTEGER;
Invalid_Operate_time EXCEPTION;
BEGIN
-- 判断周六、周日禁止操作
IF (DAYNAME(Sysdate) = 'Saturday' OR DAYNAME(Sysdate) = 'Sunday') THEN
RAISE Invalid_Operate_time;
END IF;
END;
/
-- 测试1:更新部门编号,员工表自动同步deptno
UPDATE dmoa.DEPTTAB SET Deptno=11 WHERE Deptno=10;
SELECT * FROM dmoa.EMPTAB;
-- 测试2:删除部门,员工表该部门员工全部删除
DELETE FROM dmoa.DEPTTAB WHERE Deptno=20;
SELECT * FROM dmoa.EMPTAB;
图2.8-1同步测试验证
【延伸】触发器设计原则:①希望一个操作引起一系列相关动作时用触发器;②不要用触发器重复实现约束机制已能完成的完整性检查;③避免递归触发——触发器体内的语句又使该触发器启动,导致执行无法终止;④合理控制触发器的大小与数目,任何用户任何时间执行相应操作都会触发,这是不小的开销。实践上还应注意:触发器内尽量只做轻量操作,避免在触发器体中发起长事务或调用远程 DBLINK,否则会放大事务耗时并带来难排查的锁等待。
视图是从一个或几个基表(或视图)导出的表,它是一个虚表:数据字典中只存放视图的定义(视图名和查询语句),而不存放对应的数据,数据仍存放在原来的基表中。当需要使用视图时,系统执行其对应的查询语句,导出的结果即为视图的数据。因此基表数据变化时,从视图查询出的数据也随之改变。
视图的四大作用(手册原文归纳):①用户能通过不同的视图以多种角度观察同一数据;②简化了用户操作(复杂条件查询在视图定义中一次给定);③为需要隐蔽的数据提供自动安全保护(不同用户看到不同的视图);④为重构数据库提供一定程度的逻辑独立性(如将 PRODUCT 表垂直拆分为 PRODUCT_1、PRODUCT_2 后,建立同名视图 PRODUCT 保持用户程序不变)。
视图定义语句的语法:
CREATE [OR REPLACE] [FORCE] VIEW [IF NOT EXISTS]
[<模式名>.]<视图名>[(<列名> {,<列名>})]
AS <查询说明>
[WITH [LOCAL|CASCADED] CHECK OPTION] | [WITH READ ONLY];
要点:FORCE 选项强制创建视图:基表可以不存在,创建不报错但报警告,视图处于无效状态。视图名后的列名个数必须与 SELECT 后的值表达式个数相等;当 SELECT 后出现集函数、运算表达式、多表连接的同名列,或需要为列取新名字时,视图名后的列名不能省。若同时指定 OR REPLACE 与 IF NOT EXISTS,按 OR REPLACE 策略执行。视图上可以建立 INSTEAD OF 触发器(只允许元组级触发),但不允许创建 BEFORE/AFTER 触发器。
-- 例1:对 employee 表创建视图,保存编号为 1001的员工
CREATE or REPLACE VIEW DMHR.v_employee AS
SELECT EMPLOYEE_ID, EMPLOYEE_NAME, IDENTITY_CARD, EMAIL, PHONE_NUM
FROM DMHR.EMPLOYEE
WHERE EMPLOYEE_id = 1001;
-- 例2:多基表连接视图
CREATE VIEW DMHR.v_employee01 AS
SELECT T1.employee_id, T2.department_name
FROM DMHR.employee T1, DMHR.DEPARTMENT T2
WHERE T1.DEPARTMENT_ID = T2.DEPARTMENT_ID and T1.EMPLOYEE_ID='1001';
-- 例3:分组视图(SELECT 后有集函数,必须重命名视图列)
CREATE or REPLACE VIEW DMHR.v_employee02 (DEPT_ID, SUM_SALARY) AS
SELECT DEPARTMENT_ID, COUNT(EMPLOYEE_ID)
FROM DMHR.EMPLOYEE
GROUP BY DEPARTMENT_ID;
3.2-1多基表视图
3.2-2分组视图
视图一经定义,就可以和基表一样被查询,也可以在视图之上再建新视图。当基表结构变化导致视图无效时,可以使用 ALTER VIEW ... COMPILE 重新编译视图(例如基表新增列后重新编译使视图可用):
select * from DMHR.v_employee02;; -- 查询视图
ALTER VIEW PURCHASING.VENDOR_EXCELLENT COMPILE; -- 重新编译视图
图3.3-1视图查询编译
视图是虚表,对视图的更新(INSERT/DELETE/UPDATE)均要转换成对基表的操作:系统先从数据字典取出视图定义,将查询说明与修改语句结合,转换成对基表的修改语句再执行。并非所有视图都可更新,DM 中包含以下情况的视图不支持更新:①集合运算;②分组、集函数、HAVING 子句、层次查询、TOP 子句、ROWNUM;③不可更新视图、外部表、物化视图辅助表、位图连接索引辅助表、自定义统计信息表、系统表;④超过 128 个表对象;⑤INI 参数 ENABLE_DIST_VIEW_UPDATE=0 时含 DISTINCT 的视图(COMPATIBLE_MODE=1 时强制置 1);⑥INI 参数 MULTI_UPD_OPT_FLAG 不包含 2 时含多表连接(或多表连接条件不包含聚集主键);⑦视图定义含 TOP/LIMIT/ROW_LIMIT 子句;⑧在 TABLE 函数所得表之上建立的视图;⑨在不允许更新视图之上建立的视图;⑩视图查询项未包含基表所有的聚集索引列。
【延伸】对可更新视图判据的理解:视图能否更新,本质是“能否把对视图的 DML 无歧义地转换为对单个基表的 DML”。所以凡涉及聚合、分组、集合运算、DISTINCT、ROWNUM 等“多行归并为一行”或“人为编号”的查询结果都不可更新;多表连接视图在 DM 中默认也不可更新(除非 MULTI_UPD_OPT_FLAG 包含 2 且连接条件包含聚集主键)。设计上,需要可更新的视图应尽量保持“单基表 + 简单过滤 + 包含全部聚集索引列”的形式。
WITH CHECK OPTION 用于可更新视图:往视图中 INSERT 或 UPDATE 数据时,插入行或更新行的数据必须满足视图定义中查询说明指定的条件,防止用户通过视图更新到不属于视图范围的数据。LOCAL 表示数据只需满足当前视图的条件;CASCADED 表示数据必须同时满足当前视图及所有相关视图的条件。WITH READ ONLY 指明视图为只读视图,只可查询,不可做其它 DML 操作。MPP 系统下不支持 WITH CHECK OPTION 操作。
-- 带 WITH CHECK OPTION 的视图:插入 DEPARTMENT_ID=101 以外的数据会被拒绝
CREATE or REPLACE VIEW DMHR.EMP_DEPT101_CHECK AS
SELECT EMPLOYEE_ID, EMPLOYEE_NAME, SALARY,EMAIL,HIRE_DATE,JOB_ID, DEPARTMENT_ID
FROM DMHR.EMPLOYEE
WHERE DEPARTMENT_ID = 101
WITH CHECK OPTION;
-DEPARTMENT_ID=101不满足WHERE条件,WITH CHECK OPTION拦截
INSERT INTO DMHR.EMP_DEPT101_CHECK(EMPLOYEE_ID,EMPLOYEE_NAME,SALARY,EMAIL,HIRE_DATE,JOB_ID,DEPARTMENT_ID)
VALUES(9998,'测试员工2',8000,'maxueming01@dameng.com','2008-05-30',41,102);
-- 只读视图
CREATE VIEW DMHR.v_v_employee03 as
select * from dmhr.employee a where a.employee_id='1002' with read only;
图3.5-2视图修改条件约束图
图3.5-3只读视图创建
视图本质上基于其它基表或视图的查询,系统在创建视图成功后会隐式建立对象间的依赖关系。当一个视图不被其它对象依赖时可以随时删除:
DROP VIEW [IF EXISTS] [<模式名>.]<视图名> [RESTRICT | CASCADE];
-- 示例
DROP VIEW PURCHASING.VENDOR_EXCELLENT;
图3.6-1删除视图
物化视图(Materialized View)是从一个或几个基表导出的表,同视图相比,它存储了导出表的真实数据。当基表中的数据发生变化时,物化视图所存储的数据将变得陈旧,用户可以通过手动刷新或自动刷新来对数据进行同步。物化视图以空间换时间:把复杂查询的结果预先计算并落盘存储,查询时直接读取,适合数据仓库中的预聚合报表、统计汇总等场景。
【延伸】视图 vs 物化视图:视图是虚表,不占存储,每次查询实时执行定义语句,数据永远最新;物化视图是实表(MTAB$_ 前缀的物化视图表存放真实数据),占存储,查询快但数据可能陈旧,需要刷新机制同步。物化视图上可以建索引(转为对其物化视图表建索引);视图上则不可以。选择原则:实时性要求高用视图,查询频繁且可容忍延迟用物化视图。
物化视图定义语句的语法(摘自《DM8_SQL 语言使用手册》7.1):
CREATE MATERIALIZED VIEW [<模式名>.]<物化视图名>[(<列名>{,<列名>})]
[<辅助表子句>|<预建表子句>][<物化视图刷新选项>][<查询改写选项>]
AS <查询说明>;
-- 对 EMPLOYEE 表创建物化视图,保存部门编号为101的员工
CREATE MATERIALIZED VIEW DMHR.MV_EMP_DEPT101
--第一次在当前时间进行刷新,之后隔一天刷新一次
REFRESH WITH ROWID START WITH SYSDATE NEXT SYSDATE + 1 AS
SELECT EMPLOYEE_ID, EMPLOYEE_NAME, SALARY, DEPARTMENT_ID, HIRE_DATE
FROM DMHR.EMPLOYEE
WHERE DEPARTMENT_ID = 101;
图4.2-1创建物化视图
要点:创建物化视图时会产生两个字典对象:物化视图和物化视图表(MTAB$_物化视图名,存放真实数据)。BUILD IMMEDIATE 为立即填充(默认),BUILD DEFERRED 为延迟填充(第一次刷新必须为 COMPLETE 完全刷新)。ON PREBUILT TABLE 可指定已存在的表作为物化视图的辅助表(预建表物化视图),此时物化视图删除前不能直接删除预建表。定义查询中的 ORDER BY 子句仅在创建时使用,此后被忽略。创建物化视图要求具有 CREATE MATERIALIZED VIEW 权限且至少拥有 CREATE TABLE 权限之一。
依据定义中查询语句的不同,物化视图分为六种类型,可通过系统视图 SYS.USER_MVIEWS 的 MVIEW_TYPE 列查看当前用户拥有的物化视图分类:
类型 定义特征
SIMPLE 无 GROUP BY、无集函数、无连接操作、无远程数据库表、无视图
AGGREGATE 仅包含有 GROUP BY 和集函数
JOIN 仅包含有多表连接
Sub-Query 仅包含有子查询
SETS 包含 UNION ALL
COMPLEX 除上述五种外的物化视图类型
刷新方法有三种:FAST(快速刷新,根据相关表上的数据更改记录进行增量刷新,记录存于物化视图日志,使用 FAST 刷新之前必须先建好物化视图日志)、COMPLETE(完全刷新,通过执行物化视图的定义脚本重新计算)、FORCE(默认选项,快速刷新可用时采用快速刷新,否则采用完全刷新)。
刷新时机:ON DEMAND(由用户通过 REFRESH 语法手动刷新;若指定 START WITH 和 NEXT 则按计划自动刷新)、ON COMMIT(在相关表上事务提交时进行快速刷新,由异步线程执行,COMMIT 结束后可能需要等待一段时间数据才最新;包含远程表的物化视图不支持 ON COMMIT)。START WITH 指定首次刷新时间,NEXT 指定自动刷新间隔;省略 START WITH 则首次刷新为当前时间加 NEXT 间隔;指定 START WITH 省略 NEXT 则只刷新一次;二者都未指定则不会自动刷新。
刷新规则:WITH PRIMARY KEY(要求物化视图定义中包含所有其基于的表的 PK 列,物化视图日志需记录 PK)或 WITH ROWID(要求将 ROWID 提取出来并给出别名,物化视图日志需记录 ROWID)。
-- 手动刷新物化视图(刷新语句总是自动提交,不能回滚)
REFRESH MATERIALIZED VIEW DMHR.MV_EMP_DEPT101 FAST;
REFRESH MATERIALIZED VIEW DMHR.MV_EMP_DEPT101 COMPLETE;
-- 修改物化视图刷新方式为完全刷新
ALTER MATERIALIZED VIEW DMHR.MV_EMP_DEPT101 REFRESH COMPLETE;
-- 删除物化视图(同时清除物化视图表)
DROP MATERIALIZED VIEW [IF EXISTS] [<模式名>.]<物化视图名>;
图4.3-1物化视图完全刷新
注意:物化视图的刷新语句总是自动提交的,不能回滚。用户不能直接对物化视图及物化视图表进行插入、删除、更新和 TRUNCATE 操作,对物化视图数据的修改只能通过刷新语句进行;物化视图允许查询与建索引(转为对物化视图表处理)。NEVER REFRESH 表示从不刷新,可通过 ALTER MATERIALIZED VIEW ... REFRESH 更改。DM8 目前仅语法支持 QUERY REWRITE(查询改写)选项,实际功能未实现。
物化视图的快速刷新依赖于基表上的物化视图日志,日志记录了基表的变化信息。物化视图日志只对应一个基表(与物化视图可依赖多个基表不同)。
CREATE MATERIALIZED VIEW LOG ON [<模式名>.]<表名>
[<表空间子句>][<STORAGE子句>][<WITH子句>][<PURGE选项>];
<WITH子句> ::= WITH { PRIMARY KEY | ROWID | SEQUENCE | (<列名>{,<列名>})}
-- 在 DMHR.EMPLOYEE 上创建物化视图日志,每天定时 PURGE
CREATE MATERIALIZED VIEW LOG ON DMHR.EMPLOYEE WITH
ROWID(EMPLOYEE_ID, EMPLOYEE_NAME, SALARY, DEPARTMENT_ID, HIRE_DATE)
PURGE START WITH SYSDATE + 5 REPEAT INTERVAL '1' DAY;
【PURGE START WITH SYSDATE +5:--5 天后启动日志清理任务**
REPEAT INTERVAL '1' DAY: ---之后每天执行一次 PURGE 清理物化视图日志】
-- 查询EMPLOYEE表物化视图日志,查看DML变更记录
SELECT * FROM DMHR.MLOG$_EMPLOYEE;
-- 删除物化视图日志(同时删除日志表对象和触发器对象)
DROP MATERIALIZED VIEW LOG [IF EXISTS] ON [<模式名>.]<表名>;
图4.4-1查看物化视图日志
要点:在表 T 上创建物化视图日志后会生成:①以 MLOG$_ 为前缀的日志表 MLOG$T;②以 MTRG$ 为前缀的表级触发器 MTRG$T;③定时 purge 物化视图日志的触发器 MTRG_PURGE_MVLOG<id>。默认用户只能查询 MLOG$_T,不能增删改(触发器由系统维护);设置 INI 参数 VIEW_OPT_FLAG=2 后用户可对 MLOG$_T 做增删改与 TRUNCATE。日志基表名称长度必须小于 123 个字符。
一般限制:物化视图定义只能包含用户创建的表、视图和物化视图对象,不能为外部表;同一表上最多允许建立 127 个物化视图;物化视图名称最大长度为 122(辅助表 MTAB$_ 前缀占用);包含物化视图的普通视图及游标不能更新;对基表执行 TRUNCATE、快速装载或与分区子表数据交换后,依赖它的物化视图必须先做一次完全刷新才可使用快速刷新。
快速刷新通用约束:每个基表都必须包含物化视图日志,且日志创建时间不得晚于物化视图的最后刷新时间;不能含不确定性函数(如 SYSDATE、ROWNUM);查询项不能含分析函数;不能有 HAVING 子句;不能包含 ANY、ALL 及 NOT EXISTS;不能含层次查询;不能在多个站点含相关表;不能含除 UNION ALL 外的集合运算;不能含子查询;只能基于普通表;DM8 目前仅支持简单类型和部分连接物化视图的快速刷新(外连接、自然连接不支持)。
【延伸】快速刷新的本质理解:FAST 刷新并不是“把视图定义重跑一遍”,而是基于物化视图日志中的增量记录(INSERT/DELETE/UPDATE 变化)把变化“叠加”到物化视图的存量数据上。因此对分组物化视图,查询项中一定要有 count(),且集函数依赖链必须完整(sum 依赖 count,avg 依赖 count+sum,stddev 依赖 count+sum+sum(expexp))——因为增量刷新需要这些中间量来维持可加性。理解了这一点,就能解释手册中大部分快速刷新限制条款的由来。
【延伸】物化视图使用场景建议:数据仓库日汇总报表(每日凌晨刷新)、销售统计等“计算重、变化慢”的场景收益最大。生产环境应把物化视图与刷新作业统一纳入运维:ON COMMIT 刷新会拖慢基表事务提交,务必评估;多物化视图共基表时注意日志清理策略(PURGE),避免 MLOG$_ 无限膨胀。
物化视图编译:可通过 ALTER MATERIALIZED VIEW ... COMPILE 重新编译物化视图,使用说明与视图编译类似。
序列(Sequence)是一个数据库实体,通过它多个用户可以产生唯一整数值,可以用序列来自动地生成主关键字值。序列定义语句的语法:
CREATE SEQUENCE [IF NOT EXISTS] [<模式名>.]<序列名> [<序列选项列表>];
<序列选项> ::=
INCREMENT BY <增量值> | START WITH <初值> |
MAXVALUE <最大值> | NOMAXVALUE | MINVALUE <最小值> | NOMINVALUE |
CYCLE | NOCYCLE | CACHE <缓存值> | NOCACHE |
ORDER | NOORDER | GLOBAL | LOCAL
参数要点:INCREMENT BY 指定步长(可为正负整数,不能为 0,缺省为 1),增量绝对值必须小于等于(最大值-最小值);START WITH 指定第一个生成的序列数(升序缺省为最小值,降序缺省为最大值);MAXVALUE/MINVALUE 缺省时升序最大值为 9223372036854775807、升序最小值为 1;CYCLE 为循环序列(到达最大/最小值后从最小/最大值计数),NOCYCLE 为非循环;CACHE 预先分配序列值并保持在内存中以加快访问(最小 2,最大不大于 2147483647),NOCACHE 不预先分配;ORDER 保证按请求顺序生成序列号;GLOBAL/LOCAL 用于 MPP 环境(缺省 GLOBAL,LOCAL 最高 10 位记录 MPP 节点号,其最大/最小值分别为 9007199254740991/-9007199254740992)。
缺省序列:什么都不指定则生成一个从 1 开始、增量为 1、无限上升的升序序列;仅指定 INCREMENT BY -1 则创建从 -1 开始无限下降的降序序列。创建序列要求具有 DBA 角色(三权分立)、DB_OBJECT_ADMIN 角色(四权分立)或 CREATE SEQUENCE/CREATE ANY SEQUENCE 权限。
创建序列 SEQ_QUANTITY 并将前两个值插入表:
CREATE TABLE PRODUCT_INVENTORY(
COL1 INT,
COL2 INT,
QUANTITY INT
);
CREATE SEQUENCE SEQ_QUANTITY INCREMENT BY 10; --增量为10
INSERT INTO PRODUCT_INVENTORY
VALUES(1, 1, SEQ_QUANTITY.NEXTVAL);
-- 第一次 NEXTVAL 返回 1
INSERT INTO PRODUCT_INVENTORY
VALUES(1, 1, SEQ_QUANTITY.NEXTVAL);
-- 第二次 NEXTVAL 返回 11
图5.1-1创建序列
序列一旦生成,就可以在 SQL 语句中用伪列 CURRVAL 与 NEXTVAL 存取序列的值:CURRVAL 返回当前的序列值;NEXTVAL 对升序序列增加并返回增加后的值,对降序序列减少并返回减少后的值;第一次对序列使用 NEXTVAL 时返回序列当前值。用户会话在第一次使用 CURRVAL 之前应先使用 NEXTVAL 获取序列当前值;之后除非会话再次使用 NEXTVAL,否则每次使用 CURRVAL 返回的值不变。
【延伸】CURRVAL 的会话性:CURRVAL 返回的是“当前会话最后一次 NEXTVAL 的值”,不同会话之间相互独立——会话 A 取到 5 不会影响会话 B 的 CURRVAL。这在多会话并发取号时尤为重要:不能用 CURRVAL 去推断其它会话已经取到的值。
ALTER SEQUENCE 可修改序列步长值、设置最大/最小值、改变缓存值、循环属性、ORDER 属性、当前值,以及为序列重命名:
ALTER SEQUENCE [<模式名>.]<序列名> {<序列修改选项列表> | <序列重命名选项>};
<序列修改选项> ::=
INCREMENT BY <增量值> | MAXVALUE <最大值> | NOMAXVALUE |
MINVALUE <最小值> | NOMINVALUE | CYCLE | NOCYCLE |
CACHE <缓存值> | NOCACHE | ORDER | NOORDER | CURRENT VALUE <当前值>
<序列重命名选项> ::= RENAME TO <新序列名>
-- 示例
CREATE SEQUENCE SEQ1 INCREMENT BY 1000 START WITH 5
MAXVALUE 1000000 MINVALUE 1 CACHE 10;
ALTER SEQUENCE SEQ1 INCREMENT BY 1; -- 修改步长
SELECT SEQ1.NEXTVAL FROM DUAL;
ALTER SEQUENCE SEQ1 RENAME TO SEQ1_NEW; -- 重命名
图5.3-1序列生成验证
修改步长行为说明:如果修改前没有用 NEXTVAL 访问序列,创建完序列后直接修改步长值,则再次访问时从序列初值开始按新步长取值;如果修改前已用 NEXTVAL 访问过序列,再修改步长,则再次访问时从当前值开始按新步长取值。修改序列的当前值(CURRENT VALUE)后,需要使用 NEXTVAL 获取修改后的序列当前值。重命名选项(RENAME TO)不能与其它序列修改选项一起使用。
DROP SEQUENCE [IF EXISTS] [<模式名>.]<序列名>;
-- 示例
DROP SEQUENCE SEQ_QUANTITY;
图5.4-1序列删除
【延伸】序列 vs 自增列:序列是独立数据库对象,可在多个表、多个列间共享取值,支持 CYCLE/CACHE/ORDER 等精细控制,取号与插入解耦;自增列绑定在单表的单个列上,由系统自动维护、不可更新、不可回滚。需要“全局唯一业务号”(如跨表订单号、流水号)时用序列;只需要“本表行标识单调递增”时用自增列更简单。二者都可用于生成主键,但序列更适合多表共享与可预测取号场景。
【延伸】CACHE 与 ORDER 的权衡:CACHE 能显著提升并发取号性能(内存中预分配,避免每次 NEXTVAL 都写盘),但数据库异常重启时缓存中未使用的序列值会丢失,造成“跳号”;ORDER 保证按请求顺序生成,会牺牲并发性能。绝大多数业务对“连续不跳号”并无硬性要求(主键只需唯一),因此实践中通常采用 CACHE + NOORDER 组合;只有票据号、流水号等需要严格有序的业务才用 ORDER。
同义词(Synonym)让用户能够为数据库的一个模式下的对象提供别名。同义词通过掩盖一个对象真实的名字和拥有者,并且对远程分布式的数据库对象给予位置透明特性,以此提供了一定的安全性;同时使用同义词可以简化复杂的 SQL 语句。同义词可以替换模式下的表、视图、序列、函数、存储过程等对象。
CREATE [OR REPLACE] [PUBLIC] SYNONYM [IF NOT EXISTS]
[<模式名>.]<同义词名> FOR [<模式名>.]<对象名>;
-- 示例1:用户 B 对 A 模式下的表 T1 创建同义词
CREATE SYNONYM A.S1 FOR A.T1;
SELECT COUNT(*) FROM A.S1; -- 通过同义词查询
-- 示例2:全局同义词
CREATE OR REPLACE PUBLIC SYNONYM SP_CLOSE_SESSION FOR GET_DBA2;
-- 示例3:为远程对象创建同义词(对象名通过外部链接获取)
-- 创建DBLINK:LINK1,连接远端达梦库
方式一:
CREATE LINK LINK1 CONNECT 'DAMENG' WITH sysdba IDENTIFIED BY "DAmeng123" USING '..2.62:25236';
SELECT * FROM DMOA.DMOA@LINK1;
-- 删除原来依赖MAL的LINK1
DROP LINK LINK1;
方式二:
-- 创建DPI类型外部链接,DPI不使用MAL
CREATE LINK LINK1
CONNECT 'DPI'
WITH sysdba
IDENTIFIED BY "DAmeng123"
USING '..2.62:25236';
SELECT * FROM DMOA.DMOA@LINK1;
图6.2-1同义词创建使用
要点:PUBLIC 指定全局同义词(公有同义词),创建时不能指定模式名限定词,所有用户无需加模式限定名即可使用;非全局同义词(私有同义词)被非同义词所属模式的拥有者引用时需要加模式名。用户在自己的模式下创建同义词必须有 CREATE SYNONYM 权限;创建全局同义词必须有 CREATE PUBLIC SYNONYM 权限。同义词名不能和系统对象同名;非全局同义词不能与目标对象同名;全局同义词与非全局同义词可以同名。同义词创建时并不检查所指代对象是否存在,使用时若不存在或没有权限则报错。
用户使用 SQL 语句对某个模式下的同义词进行操作时,解析该对象的顺序是:首先查看模式内是否存在该同义词对象(若未指定模式名,则指当前默认模式),然后再查看是否存在同名全局同义词。
手册示例:用户 OE 和 SH 的模式下都有一个表叫 customer,SYSDBA 为 OE 模式下的 customer 表创建了全局同义词 customer_syn,为 SH 模式下的 customer 表创建了非全局同义词 customer_syn。用户 SH 查询 SELECT COUNT(*) FROM customer_syn 时,返回的是全局同义词指向对象 OE.CUSTOMER 表的行数(因为先查模式内 SH.customer_syn,再查全局同义词,而 SH 模式下没有自己的 customer_syn 私有同义词时……实际手册结论是此时返回全局同义词结果——解析逻辑为:优先查找指定模式下的目标同义词对象,若查找失败则查找同模式下的同名对象,最后查找同名全局同义词对象)。
补充:若目标对象未显式指定模式名,则在创建该同义词的模式下进行对象查找。若模式对象的非全局同义词与系统对象同名,在此模式下不加模式名前缀调用时,系统函数的优先级比非全局同义词高,系统包和系统过程的优先级比非全局同义词低。当 INI 参数 ENABLE_PL_SYNONYM=0 时,禁止通过同义词执行非系统用户创建的包或 DMSQL 程序。
-- 演示同义词解析顺序(简化场景):
-- 先查模式内私有同义词,未命中再查全局同义词
CREATE SYNONYM LOCAL_T FOR DMOA.t01; -- 私有同义词 LOCAL_T
CREATE PUBLIC SYNONYM LOCAL_T FOR DMOA.t01; -- 全局同义词 LOCAL_T(允许与私有同名)
-- 模式 LOCAL 下查询 LOCAL_T:命中私有同义词,返回 DMOA.t01 数据
-- 其它模式查询 LOCAL_T:模式内无同名对象,命中全局同义词,返回 PUBLIC.t01 数据
SELECT COUNT(*) FROM LOCAL_T;
图6.3-1创建全局同义词
DROP [PUBLIC] SYNONYM [IF EXISTS] [<模式名>.]<同义词名>;
-- 示例1:删除模式 A 下的同义词 S1
DROP SYNONYM A.S1;
-- 示例2:删除全局同义词 S2(必须指定 PUBLIC)
DROP PUBLIC SYNONYM S2;
图6.4-1删除同义词
要点:删除全局同义词必须指定 PUBLIC(因为全局与非全局同义词可以同名,不指定则删除的是当前模式下的同名同义词);删除非全局同义词则不能指定 PUBLIC,否则报错。必须是 DBA 或拥有此同义词的用户才能删除。
【延伸】同义词的典型应用(手册之外的实践归纳):①数据库迁移/重构时保持应用 SQL 不变——应用引用同义词,真实对象指向可随时切换;②多环境(开发/测试/生产)通过同义词指向不同目标库,简化部署脚本;③隐藏真实表名与所有者,提升安全性;④为远程 DBLINK 对象建立本地短名,实现位置透明(结合第 8 章外部链接)。注意:同义词不提供任何权限提升能力,用户对目标对象仍需拥有相应权限。
DM 提供两种自增方式:IDENTITY 自增列和 AUTO_INCREMENT 自增列。IDENTITY 自增列与 CREATE TABLE 语句一起使用,在表中创建一个自增列,一个表只能有一个自增列。
IDENTITY [ (种子, 增量) ]
-- 示例:建表时定义自增列
CREATE TABLE PERSON.PERSON_TYPE
(
PERSON_TYPEID INT IDENTITY(1, 1), -- 自增列:种子 1,增量 1
NAME VARCHAR(30)
);
图7.1-1创建序列
使用说明:①IDENTITY 适用于 INT、BIGINT、DEC(n,0) 类型的列,每个表只能创建一个自增列;②不能对自增列使用 DEFAULT 约束;③必须同时指定种子和增量值,或者二者都不指定,二者都未指定则取默认值 (1,1);种子或增量为小数类型时报错;④最大值和最小值为该列的数据类型的边界,建表种子和增量超出边界时报错;⑤自增列一旦生成无法更新,不允许用 UPDATE 语句修改;⑥自增列的值一旦生成无法回滚——例如当前值为 n、增量为 1,插入时若约束检查失败回滚插入操作,自增列的值 n+1 已经生成无法回滚,当前值变为 n+1;⑦临时表、列存储表不支持使用自增列。
IDENT_SEED('tablename') 返回种子值(创建自增列时指定的第一个行使用的值);IDENT_INCR('tablename') 返回增量值。参数 tablename 为 CHAR/VARCHAR 类型的字符串常量、变量、函数或列名,可带模式名前缀。
SELECT IDENT_SEED('PERSON.PERSON_TYPE'); -- 返回 1
SELECT IDENT_INCR('PERSON.PERSON_TYPE'); -- 返回 1
图7.2-1序列使用
默认情况下自增列的值由系统自动生成。当需要将显式值插入表的自增列时(如数据迁移、指定历史主键),必须先将 IDENTITY_INSERT 属性设置为 ON:
SET IDENTITY_INSERT [<模式名>.]<表名> ON [WITH REPLACE NULL];
SET IDENTITY_INSERT [<模式名>.]<表名> OFF;
-- 完整示例(手册原文):
-- 1) 自动生成自增列值
INSERT INTO PERSON_TYPE(NAME) VALUES('销售总监');
INSERT INTO PERSON_TYPE(NAME) VALUES('人力资源部经理');
-- 此时 PERSON_TYPEID 自动为 5、6
-- 2) 显式指定自增列值(必须先置 ON,且插入列表必须明确指定自增列)
SET IDENTITY_INSERT PERSON_TYPE ON;
INSERT INTO PERSON_TYPE(PERSON_TYPEID, NAME) VALUES(8, '广告部经理');
INSERT INTO PERSON_TYPE(PERSON_TYPEID, NAME) VALUES(9, '财务部经理');
-- 3) 自增列不允许 UPDATE
UPDATE PERSON_TYPE SET PERSON_TYPEID = 9 WHERE NAME = '广告部经理';
-- 修改失败:对于自增列,不允许 UPDATE 操作
-- 4) 还原 IDENTITY_INSERT 属性
SET IDENTITY_INSERT PERSON_TYPE OFF;
-- 5) WITH REPLACE NULL 模式:允许显式插入 NULL,系统自动替换为自增值
SET IDENTITY_INSERT PERSON_TYPE ON WITH REPLACE NULL;
INSERT INTO PERSON_TYPE(PERSON_TYPEID, NAME) VALUES(NULL, '总经理');
图7.3-1自增序列使用
要点:①IDENTITY_INSERT 属性默认 OFF,设置是在执行或运行时进行的,一个连接结束自动还原为 OFF;②DM 要求一个会话中只有一个表的 IDENTITY_INSERT 可设置为 ON,设置新表为 ON 时之前为 ON 的表自动还原为 OFF;③ON 状态下,如果插入值大于表的当前标识值,DM 自动将新插入值作为当前标识值(改变自增列当前值);否则不影响;④OFF 状态下新插入行自增列值由系统自动生成;⑤手动插入自增列时必须在插入列表中明确指定自增列列名;⑥WITH REPLACE NULL 模式下允许显式插入 NULL 值,系统自动将 NULL 替换为自增值。
【延伸】自增列的“不连续”现象与处理(手册之外):由于自增列值“一旦生成无法回滚”,事务回滚、失败插入都会造成主键跳号,这是设计使然,不应依赖自增列值连续。数据迁移或需要保留原主键时,先 SET IDENTITY_INSERT ... ON 显式插入原值(大于当前值会抬升自增列的当前值),插入完成后置 OFF 即可继续自动递增。注意一个会话同时只能有一个表处于 ON 状态,多表迁移需逐个处理。
外部链接对象(LINK)是 DM 中的一种特殊数据库实体对象,它记录了远程数据库的连接和路径信息,用于建立与远程数据的联系。通过多台数据库主库间的相互通讯,用户可以透明地操作远程数据库的数据,使应用程序看起来只有一个大型数据库。用户远程数据库中的数据请求都被自动转换为网络请求,并在相应节点上实现相应操作。链接可以是公用的(PUBLIC,数据库中所有用户使用),也可以是私有的(只能被某个用户使用)。用户可以通过外部链接对远程数据库的表进行查询和增删改操作,以及本地调用远程的存储过程。
创建外部链接的语法(摘自《DM8_SQL 语言使用手册》16.1):
CREATE [OR REPLACE] [PUBLIC] LINK [IF NOT EXISTS] <外部链接名>
CONNECT ['<连接库类型>'] WITH <登录名> IDENTIFIED BY <登录口令>
USING '<外部连接串>' [<OPTION子句>];
<连接库类型> ::= DPI | ODBC | DAMENG | ORACLE | GATEWAY
<DPI外部链接串> ::= <IP地址>:<端口号> | <服务名>
<DAMENG外部链接串> ::= [<连接类型>;]<服务器列表>
<ORACLE外部链接串> ::= <tsn_name> | <description> | <IP地址>/<服务名>
连接库类型说明:DPI 与 DAMENG 用于连接达梦数据库(DPI 不需要额外配置,是 DM-DM 同构环境推荐方式;DAMENG 通过 MAL 系统访问,需配置 DMMAL.INI 并将 DM.INI 的 MAL_INI 置 1);ODBC 可以连接所有支持 ODBC 协议的数据库(DSN 需手动配置);ORACLE 用于连接 ORACLE 数据库;GATEWAY 使用达梦的 ODBC 驱动,通过 GATEWAY 中转连接到远程数据库。
DAMENG 连接类型:PRIMARY FIRST 主机优先连接(缺省);STANDBY FIRST 备机优先连接;PRIMARY ONLY 只连主机;STANDBY ONLY 只连备机。一旦指定连接类型,必须指定不少于 2 个服务器地址。服务器地址支持三种格式:<实例IP>/<实例端口号>(对应 mal_inst_host/mal_inst_port)、<MAL IP>/<MAL端口号>(对应 mal_host/mal_port)、实例名(对应 mal_inst_name)。
OPTION 子句:LOCAL_CODE 接口字符集(UTF-8/GBK/GB18030);CONVERT_MODE 不完整字符处理(0 截断不报错,1 报错);BYTES_IN_CHAR 一个字符的字节数;DB_TYPE 目标数据库类型(SQLSERVER/MYSQL/PI/ORACLE/DAMENG/OCEANBASE_ORACLE,仅 ODBC 有效);DATA_CHARSET 接口返回数据的实际字符集;CASE_OPT 字符串比较规则('UPPER'/'LOWER'/'MIX'/'SENSITIVE');ROLLBACK_OPTION 回滚级别(0 语句级回滚,1 事务级回滚,仅 oci 和 dpi 类型有效)。
创建示例(手册原文):
-- 例1:DPI 外部链接串连接 DM 数据库
CREATE LINK LINK1 CONNECT 'DPI' WITH sysdba
IDENTIFIED BY "DAmeng123"
USING '..2.62:25236';
SELECT * FROM DMOA.DMOA@LINK1;
图8.2-1外部连接创建使用
通过外部链接,可以对远程服务器的对象进行查询或增删改操作,可以调用远程的过程或函数。指定外部链接对象时使用如下格式:<对象名>@<外部链接名>。<对象名>可以为远程服务器的表、视图、序列、存储过程或函数的名称。
-- 查询远程表
SELECT * FROM SYSOBJECTS@LINK1;
-- 对远程表插入
-- 插入远端DMOA.DMOA
INSERT INTO DMOA.DMOA@LINK1 VALUES(2,'李四',TO_TIMESTAMP('2026-09-16','yyyy-mm-dd'));
COMMIT;
SELECT * FROM DMOA.DMOA@LINK1;
-- 调用远程存储过程或函数(参数须为 SQL 类型,不允许 DMSQL 程序类型和复合类型)
[<模式名>.] [<包名>.] <过程/函数名> @ <外部链接名>(<参数列表>);
-- 删除外部链接
DROP [PUBLIC] LINK [IF EXISTS] [<模式名>.]<外部链接名>;
图8.3-1外部连接使用验证
要点:通过 LINK 对远程服务器所作的修改,由用户在本地服务器通过 COMMIT 或 ROLLBACK 进行提交或回滚;只支持普通用户,不支持 SSL 和 Kerberos 认证;不支持连接自身实例的 LINK;支持在 CREATE SCHEMA 中 CREATE LINK,但不支持 CREATE PUBLIC LINK;只有 DBA 和具有 CREATE LINK 权限的用户可以创建外部链接。
同构(DM-DM)外部链接:不支持 MPP 环境;增删改不支持 INTO 语句;不支持使用游标进行增删改操作;不支持操作远程表的复合类型列;理论上不支持 LOB 类型列的操作(但支持简单增删改语句中使用常量对 LOB 列操作);本地库与远程库的 CASE_SENSITIVE(大小写敏感)参数应保持一致,字符集编码应一致,否则可能导致字符串操作出错。
异构数据库外部链接:数据类型以 DM 为基础,不支持 DM 没有的数据类型;语法以 DM 的语法为标准;主键更新若涉及多个服务器不能保证一定成功;使用 CREATE VIEW view_name(view_col_name) AS SELECT ITEM FROM T@LINK 方式创建的查询远程对象的本地视图,对于异构库不能保证操作一定成功,最好采用 CREATE VIEW AS SELECT ITEM AS alias_name FROM T@LINK 方式创建。
【延伸】外部链接的典型应用与排障(手册之外):典型应用:①跨库数据同步(INSERT INTO LOCAL SELECT * FROM REMOTE@LINK);②分布式应用透明访问;③异构数据迁移(Oracle→DM 数据抽取)。排障要点:①连接不上先检查网络与远程库监听、账号权限(尤其 Oracle 需配置网络服务名/驱动 OCI);②字符集不一致时先按远程库规范书写 SQL,再通过 OPTION(LOCAL_CODE=...) 调整;③CASE_SENSITIVE 不一致时,建议本地与远程统一,否则大小写敏感规则差异会导致查不到数据;④批量跨库操作注意本地事务边界——远程修改最终由本地 COMMIT 控制,事务期间可能持有远程资源锁。
(1)触发器:能用约束完成的完整性检查绝不用触发器;触发器体内禁用 COMMIT/ROLLBACK,保持轻量;数据批量装载前 DISABLE 触发器、装载后 ENABLE;审计场景优先考虑内置审计,触发器审计只作为补充。
(2)视图:用视图做权限隔离与逻辑独立性是成本最低的手段;需要可更新的视图保持“单基表+简单过滤+含聚集索引列”;防止越界更新必须加 WITH CHECK OPTION;只读场景加 WITH READ ONLY 更安全。
(3)物化视图:适合计算重、变化慢、容忍延迟的报表场景;快速刷新依赖物化视图日志,务必规划日志清理(PURGE)策略;ON COMMIT 刷新慎用,会拖慢基表事务;基表 TRUNCATE 后必须先完全刷新才能快速刷新。
(4)序列与自增列:主键生成优先考虑自增列(简单);多表共享取号、流水号用序列;CACHE 提性能但重启会跳号,ORDER 保顺序但损并发,按业务取舍;自增列值不可回滚、不可更新,迁移数据时用 SET IDENTITY_INSERT。
(5)同义词:用于对象别名、位置透明与迁移屏蔽;全局同义词与私有同义词可同名,删除全局同义词必须指定 PUBLIC;注意系统对象与非全局同义词同名时的优先级规则。
(6)外部链接:DM-DM 同构优先用 DPI 连接串(免配置);本地与远程的 CASE_SENSITIVE、字符集必须一致;远程 DML 受本地事务控制;异构场景注意数据类型与语法以 DM 为标准。
综上,七类数据库对象各司其职:触发器承载自动业务逻辑,视图与物化视图分别以“虚”与“实”两种形态解决查询与性能问题,序列与自增列解决标识生成,同义词提供对象别名与位置透明,外部链接打通跨库访问。掌握它们的作用与基本操作,是 DBA 进行应用设计、数据迁移与日常运维的必备技能。
七类对象作用与基本操作一览:
对象 核心作用 基本操作(创建/使用/删除)
触发器 事件自动执行业务逻辑(表/DDL/系统/定时) CREATE TRIGGER / ALTER TRIGGER(DISABLE|ENABLE|COMPILE) / DROP TRIGGER
视图 虚表:多角度观察、简化操作、安全保护、逻辑独立 CREATE [FORCE] VIEW ... AS ... / ALTER VIEW COMPILE / DROP VIEW
物化视图 实表预存查询结果,加速复杂报表 CREATE MATERIALIZED VIEW / REFRESH / ALTER / DROP
序列 多用户共享生成唯一整数,自动生成主键 CREATE SEQUENCE / NEXTVAL / ALTER / DROP
同义词 对象别名、位置透明、简化 SQL CREATE [PUBLIC] SYNONYM ... FOR ... / DROP [PUBLIC] SYNONYM
自增列 单表行标识自动递增,不可更新不可回滚 IDENTITY(种子,增量) / SET IDENTITY_INSERT ON|OFF
外部链接 透明操作远程数据库,跨库查询/DML/调用存储过程 CREATE [PUBLIC] LINK / 对象名@链接名 / DROP [PUBLIC] LINK
[1] 达梦数据库. 《DM8_SQL 语言使用手册》(V8)[Z]. 5.6 自增列的使用; 6 视图; 7 物化视图; 14 触发器; 15 同义词; 16 外部链接.
[2] 达梦数据库. 《DM8_SQL 语言使用手册》(V8)[Z]. 3.10 管理序列.
文章
阅读量
获赞
