DM存储过程及系统 表,系统视图,以及常规的运维SQL语句

DM存储过程及系统 表,系统视图,以及常规的运维SQL语句

文章目录

  • [DM存储过程及系统 表,系统视图,以及常规的运维SQL语句](#DM存储过程及系统 表,系统视图,以及常规的运维SQL语句)
    • 一、存储过程、触发器、包、游标
      • 1.1、存储过程
      • 1.2、触发器
      • 1.3、包
      • [1.4 游标](#1.4 游标)
        • [1.4.1 静态游标](#1.4.1 静态游标)
        • [1.4.2 动态游标](#1.4.2 动态游标)
        • [1.4.3 使用静态游标更新、删除数据](#1.4.3 使用静态游标更新、删除数据)
      • [1.5 动态 SQL](#1.5 动态 SQL)
    • 二、系统表、系统视图
      • [2.0 DM 数据库管理常用 SQL 命令](#2.0 DM 数据库管理常用 SQL 命令)
      • [2.1 REDO 日志信息](#2.1 REDO 日志信息)
      • [2.2 表空间大小信息](#2.2 表空间大小信息)
      • [2.3 Undo 回滚段](#2.3 Undo 回滚段)
      • [2.4 死锁、阻塞、占用资源最多的SQL排查](#2.4 死锁、阻塞、占用资源最多的SQL排查)
      • [2.5 统计信息](#2.5 统计信息)

大家想学习达梦数据库或者解决报错问题,可以去 达梦数据库社区https://eco.dameng.com

学习目标

1、了解存储过程、触发器、包、游标等对象的基本操作。

2、 了解DM系统表和系统视图。

3、 基于这些系统对象,能够了解到哪些信息。

4、基于这些系统对象,可以加工得到哪些信息。

一、存储过程、触发器、包、游标

1.1、存储过程

存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的 SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。

一般经常用来,处理业务、控制交易类型的复杂事务提交或者回滚。

曾经有的项目没有使用消息队列做业务某些功能的缓存,SQL全部走硬解析耗时很长。

有金融类项目应用端的发版,就使用使用存储过程控制版本更新。

创建带参数存储过程

sql 复制代码
-- 创建测试表 test_tab
create table test_tab (id int primary key, name varchar(30));

-- 创建有参数存储过程 p_test,循环写入一些数据
-- 创建有参数存储过程 p_test,循环写入一些数据
create or replace procedure p_test(i in int)
as
    j int;
begin
    for j in 1 .. i loop
        insert into test_tab values (j, 'p_test' | | j);
    end loop;
end;

-- 调用存储过程
call p_test(3);
-- 对表 test_tab 进行查询
select * from SYSDBA.test_tab limit 10;

查看实际的执行计划

bash 复制代码
服务器 [192.168.157.140:5236]:处于普通打开状态
登录使用时间  : 20.687(ms)
disql V8
SQL> SET SCHEMA "SYSDBA";
操作已执行
已用时间: 2.152(毫秒). 执行号:0.
SQL> SET DEFINE OFF;
SQL> SF_SET_SESSION_PARA_VALUE('MONITOR_SQL_EXEC',1);
DMSQL 过程已成功完成
已用时间: 0.918(毫秒). 执行号:7201.
SQL> SET AUTOTRACE TRACEONLY;
SQL> select * from SYSDBA.test_tab limit 10;

10 rows got


1 NSET2:[1, 1->10, 64]
2   PRJT2:[1, 1->10, 64];exp_num(3), is_atom(FALSE); INFO_BITS(0)
3     TOPN2:[1, 1->10, 64]
4       CSCN2:[1, 1->100, 64];INDEX33556188(TEST_TAB); btr_scan(1); need_slct(0)

# 解释
NSET2: 结果集收集,一般是查询计划的顶层节点
PRJT2: 关系的"投影"(project)运算,用于选择表达式项的计算
exp_num 映射列数
is_atom 是否要求单行数据

TOPN2: 取前N条记录
top_num TOP子句的行数表达式
top_off top_num的偏移量,例如:op_off=3、top_num=4时,从第3行后开始,取第4到第7共4行结果
top_percent top_num的百分比,例如:top_percent=0.5、top_num=100时,取前50行结果(参数top_off和top_percent不会同时出现)

CSCN2: 聚集索引扫描
idxname(tabname) 索引名(表名)
NEED_SLCT(TRUE) 是否进行过滤条件下推的优化
btr_scan 等于1表示使用B树扫描,等于0表示使用簇游标扫描
SAMPLE标记 用于标记节点上是否存在sample表达式,存在则带有此标记,否则无



# 分析完后,记得关闭,以免影响性能
SP_SET_PARA_VALUE(1,'ENABLE_MONITOR',0);
SP_SET_PARA_VALUE(1,'MONITOR_SQL_EXEC',0);'

创建不带参数存储过程

sql 复制代码
-- 创建无参数存储过程 p_test2
create or replace procedure p_test2
as
    j int;
begin
    for j in 101 .. 130 loop
        insert into test_tab values (j, 'p_test2:' || j);
        -- 判断:当 j 是 10 的倍数时,提交一次
        if mod(j, 10) = 0 then
            commit;
        end if;
    end loop;
    commit;
end;

-- 调用存储过程,查看结果
call p_test2;
select * from TEST_TAB order by id desc limit 3;

1.2、触发器

常用触发器:表级触发器、时间触发器

表级触发器的触发动作是三种数据操作命令,即 INSERT、DELETEUPDATE 操作,可以借助AFTER、BEFORE行为完成对数据的审计或者其他更高级的应用。

sql 复制代码
-- 行级触发器,对触发命令所影响的每一条记录都激发一次
CREATE OR REPLACE TRIGGER TRG_NAME
AFTER INSERT OR DELETE OR UPDATE ON TABLE_NAME
FOR EACH STATEMENT -- 语句级:此子句可省略
BEGIN
 PRINT 'INSERT OR DELETE OR UPDATE OPERATION ON TABLE_NAME';--要执行的SQL
END;

语句级触发器,对每个触发命令执行一次。

  • 创建 BEFORE 触发器,该触发器在插入一条记录前,将记录中第一列 READER_ID 的值加 1。
sql 复制代码
--创建 BEFORE 触发器
CREATE OR REPLACE TRIGGER TRG_INS_BEFORE
BEFORE INSERT ON OTHER.READER
FOR EACH ROW
BEGIN
:NEW.READER_ID:=:NEW.READER_ID+1;
END;

具体语句

sql 复制代码
-- 最终在表中,插入的 READER_ID 值为 13 ,插入时触发:NEW.READER_ID:=:NEW.READER_ID+1,提交的 READER_ID 值为 14。
insert into OTHER.READER(READER_ID, NAME, AGE, GENDER, MAJOR)
VALUES(13, 'test', 20, 'F', 'History');

COMMIT;
  • 创建 AFTER 触发器,该触发器在插入一条记录后,将插入的值以及操作类型记录到用于审计的表 T_TEMP
sql 复制代码
CREATE TABLE T_TEMP(C1 INT,C2 CHAR(20));

CREATE OR REPLACE TRIGGER TRG_INS_AFTER
AFTER INSERT ON OTHER.READER
FOR EACH ROW
BEGIN
 INSERT INTO T_TEMP VALUES(:NEW.READER_ID, 'INSERT ON READER');
END;
  • 创建 INSTEAD OF 触发器,该触发器在动作触发的时候,替换原始操作,INSTEAD OF 允许建立在视图上,并且只支持行级触发
sql 复制代码
-- 在视图 v1 上创建 INSTEAD OF 触发器,对视图的更新被替换成了对表的更新,不常用。
CREATE OR REPLACE TRIGGER tri1
INSTEAD OF UPDATE ON v1
BEGIN
 insert into t1 values(111,111); --替换动作
END;

时间触发器

时间触发器属于一种特殊的事件触发器,可以定义一些有规律性执行的、定点执行的任务。

时间触发器的最低时间频率精确到分钟级,定义很灵活,完全可以实现数据库中的代理功能,在触发器体中定义要做的工作,可以定义操作的包括执行一段 SQL 语句、执行数据库备份、执行重组 B 树、执行更新统计信息、执行数据迁移 (DTS)。

sql 复制代码
-- 每分钟输出一次hello world
CREATE OR REPLACE TRIGGER timer2
AFTER TIMER on database
for each 1 day for each 1 minute
BEGIN
print 'HELLO WORLD';
END;

触发器管理

sql 复制代码
--关闭触发器
ALTER TRIGGER OTHER.TRG_INS_AFTER DISABLE;
--打开触发器
ALTER TRIGGER OTHER.TRG_INS_AFTER ENABLE;
-- 删除触发
DROP TRIGGER OTHER.TRG_INS_AFTER;

--查看当前数据库的全部触发器
SELECT * FROM DBA_TRIGGERS;
--查看当前用户有权限访问的触发器
SELECT * FROM ALL_TRIGGERS;
--查看示当前用户所拥有的触发器
SELECT * FROM USER_TRIGGERS;

重新对触发器进行编译,如果重新编译失败,则将触发器置为禁止状态。

sql 复制代码
 ALTER TRIGGER [<模式名>.]<触发器名> COMPILE;

1.3、包

DM 支持 DMSQL 程序包来扩展数据库功能,用户可以通过包来创建应用程序或者使用包来管理过程和函数

真正使用起来有点像java开发那种面向对象的编程,一般情况下,很少使用包来控制,应用端有自己的架构

包的创建包括包规范包主体的创建。

包规范语法格式

sql 复制代码
CREATE [OR REPLACE] PACKAGE [IF NOT EXISTS] [<模式名>.]<包名> [WITH ENCRYPTION] [<调用者权限>] AS|IS <包内声明列表>  END [<包名>]
<调用者权限>::=
	AUTHID DEFINER |  
	AUTHID CURRENT_USER
<包内声明列表> ::= <包内声明>;{<包内声明>;}
<包内声明> ::= <变量声明>|<游标定义>|<异常定义>|<过程定义>|<函数定义>
<游标定义> ::= CURSOR <游标名> [FOR <查询语句>]
<异常定义> ::= <异常名> EXCEPTION [FOR <错误号> [,<错误描述>]]
<过程定义> ::= PROCEDURE <过程名> <参数列表>
<函数定义> ::= FUNCTION <函数名><参数列表> RETURN <返回值数据类型>[RESULT_CACHE] [DETERMINISTIC] [PIPELINED]

使用说明

  1. 创建包的名称不能与系统创建的模式名称相同;
  2. 包部件可以以任意顺序出现,其中的对象必须在引用之前被声明;
  3. 过程和函数的声明都是前向声明,包规范中不包括任何实现代码;
  4. < 赋值标识 > 中 DEFAULT、ASSIGN 和:=均用于为变量赋值,三种赋值标识功能和用法完全一样;
  5. < 变量声明 > 与 DMSQL 程序中的变量声明方式一致,具体请参考《DM8_SQL 程序设计》手册"3.5 变量"章节。

包主体中包含了在包规范中的前向子程序声明相应的代码。它的创建语法如下。

sql 复制代码
CREATE [OR REPLACE] PACKAGE BODY [<模式名>.]<包名> [WITH ENCRYPTION] AS|IS <包体部分> 
END [<包名>]
<包体部分> ::= <包体声明列表> [<初始化代码>]
<包体声明列表> ::=<包体声明>;[{<包体声明>;}]
<包体声明>::=<变量声明>|<游标定义>|<异常定义>|<过程定义>|<函数定义>|<存储过程实现>|<函数实现>
<游标定义> ::= CURSOR <游标名> [FOR <查询语句>]
<异常定义> ::= <异常名> EXCEPTION [FOR <错误号> [, <错误描述>]]
<过程定义> ::= PROCEDURE <过程名> <参数列表>
<函数定义> ::= FUNCTION <函数名> <参数列表> RETURN <返回值数据类型>
<存储过程实现> ::= PROCEDURE <过程名> <参数列表>  AS|IS  BEGIN <实现体> END [<过程名>];
<函数实现> ::= FUNCTION <函数名><参数列表> RETURN <返回值数据类型>[DETERMINISTIC] [PIPELINED]<AS|IS> BEGIN <实现体> END [<函数名>];
<初始化代码> ::= [[<说明部分>]BEGIN<执行部分>[<异常处理部分>]]
<说明部分> ::=[DECLARE]<说明定义>{<说明定义>}
<说明定义>::=<变量列表说明>|<异常变量说明>|<子游标定义>|<子过程定义>|<子函数定义>;
<变量列表说明>::= <变量初始化>{<变量初始化>}
<异常变量说明>::=<异常变量名>EXCEPTION[FOR <错误号> [, <错误描述>]]
<子游标定义>::=CURSOR <游标名> [FOR<查询表达式>|<连接表>]
<子过程定义>::=PROCEDURE<过程名>[(<参数列>)]<IS|AS><模块体>
<子函数定义>::=FUNCTION<函数名>[(<参数列>)]RETURN<返回数据类型><IS|AS><模块体>
<执行部分>::=<SQL过程语句序列>{< SQL过程语句序列>}
<SQL过程语句序列>::=[<标号说明>]<SQL过程语句>;
<标号说明>::=<<<标号名>>>
<SQL过程语句>::=<SQL语句>|<SQL控制语句>
<异常处理部分>::=EXCEPTION<异常处理语句>{<异常处理语句>}
<异常处理语句>::= WHEN <异常名> THEN <执行部分>;

应用实例

sql 复制代码
-- 创建一张表写入一些数据
CREATE TABLE Person(Id INT IDENTITY, Name VARCHAR(100), City VARCHAR(100));
INSERT INTO Person(Name, City) VALUES('TOM','武汉');
INSERT INTO Person(Name, City) VALUES('JACK','北京');
INSERT INTO Person(Name, City) VALUES('MARY','上海');
COMMIT;

-- 创建包头,包名PersonPackage
CREATE OR REPLACE PACKAGE PersonPackage AS
    E_NoPerson EXCEPTION;        -- 自定义异常
    PersonCount INT;             -- 全局整型变量
    Pcur CURSOR;                 -- 全局游标
    PROCEDURE AddPerson(Pname VARCHAR(100), Pcity varchar(100)); -- 添加人员存储过程
    PROCEDURE RemovePerson(Pname VARCHAR(100), Pcity varchar(100)); -- 按姓名城市删除
    PROCEDURE RemovePerson(Pid INT); -- 按ID删除(重载)
    FUNCTION GetPersonCount RETURN INT; -- 获取人数函数
    PROCEDURE PersonList; -- 查询人员列表存储过程
END PersonPackage; -- 包头结束

以下是一个包主体的实例,它对应于前面的包规范定义,包括 4 个子过程和 1 个子函数的代码实现。在包主体的末尾,是这个包对象的初始化代码。当一个会话第一次引用包时,变量 PersonCount 被初始化为Person表中的记录数。

sql 复制代码
-- 创建包体,实现包头声明的内容
CREATE OR REPLACE PACKAGE BODY PersonPackage AS
    -- 新增人员存储过程
    PROCEDURE AddPerson(Pname VARCHAR(100), Pcity varchar(100)) AS
        BEGIN
            INSERT INTO Person(Name, City) VALUES (Pname, Pcity); -- 插入数据
            PersonCount := PersonCount + SQL %ROWCOUNT; -- 【关键】更新人数:+影响行数
        END AddPerson;
        
    -- 按姓名、城市删除
    PROCEDURE RemovePerson(Pname VARCHAR(100), Pcity varchar(100)) AS
        BEGIN
            DELETE FROM Person WHERE NAME LIKE Pname AND City like Pcity;
            IF SQL %ROWCOUNT = 0 THEN -- 【关键】没删到数据
                RAISE E_NoPerson; -- 抛出自定义异常
            END IF;
            PersonCount := PersonCount - SQL %ROWCOUNT; -- 人数减少
        END RemovePerson;
        
    -- 按ID删除
    PROCEDURE RemovePerson(Pid INT) AS
        BEGIN
            DELETE FROM Person WHERE Id = Pid;
            IF SQL %ROWCOUNT = 0 THEN
                RAISE E_NoPerson;
            END IF;
            PersonCount := PersonCount - SQL %ROWCOUNT;
        END RemovePerson;
        
    -- 返回人数
    FUNCTION GetPersonCount RETURN INT AS
        BEGIN
            RETURN PersonCount; -- 【关键】返回全局变量
        END GetPersonCount;
        
    -- 遍历打印人员列表
    PROCEDURE PersonList AS
        DECLARE
        V_id   INT;
        V_name VARCHAR(100);
        V_city VARCHAR(100);
        BEGIN
            IF PersonCount = 0 THEN
                RAISE E_NoPerson;
            END IF;
            OPEN Pcur FOR SELECT Id, Name, City FROM Person; -- 打开游标
            LOOP
                FETCH Pcur INTO V_id, V_name, V_city; -- 取数据
                EXIT WHEN Pcur %NOTFOUND; -- 读完退出
                PRINT ('No.' || V_id || ' ' || V_name || '来自' || V_city);
            END LOOP;
            CLOSE Pcur; -- 关闭游标
        END PersonList;
-- 【关键】包初始化:启动时查询总人数
BEGIN
    SELECT COUNT(*) INTO PersonCount FROM Person;
END PersonPackage;

重新编译包:

sql 复制代码
ALTER PACKAGE PersonPackage COMPILE;

调用包中的 AddPerson 过程,往数据表中增加一条记录:

sql 复制代码
CALL PersonPackage.AddPerson ('BLACK', '南京') ;

调用包中的 RemovePerson 过程,删除第二条记录:

sql 复制代码
CALL PersonPackage.RemovePerson ('JACK', '北京') ;
-- 或者
CALL PersonPackage.RemovePerson (2) ;

引用包中的变量。

sql 复制代码
SELECT PersonPackage.PersonCount;

或者

sql 复制代码
SELECT PersonPackage.GetPersonCount;

1.4 游标

DMSQL 程序中使用 SELECT...INTO 语句将查询结果存放到变量中进行处理的方法,**但这种方法只能返回一条记录,否则就会产生 TOO_MANY_ROWS 错误,**为了解决这个问题,DMSQL 程序引入了游标,允许程序对多行数据进行逐条处理

例如:

sql 复制代码
DECLARE
	p_name VARCHAR(50);
	p_publish VARCHAR(50);
BEGIN
	SELECT NAME,PUBLISHER INTO p_name,p_publish FROM PRODUCTION.PRODUCT WHERE AUTHOR LIKE '曹雪芹,高鹗';
	PRINT p_name;
	PRINT p_publish;
EXCEPTION
	WHENNO_DATA_FOUND OR TOO_MANY_ROWS THEN
		PRINT'NO_DATA_FOUND OR TOO_MANY_ROWS';
	WHEN OTHERS THEN
		PRINT 'ERROR OCCURS';
END;

SELECT...INTO 语句要求查询只能返回一条记录,执行时可能会发生两种例外情况:

  • 没有查询到满足条件的记录,系统返回预定义异常 NO_DATA_FOUND;
  • 存在多行满足条件的记录,系统返回预定义异常 TOO_MANY_ROWS,对于这样的异常必须做出相应处理,否则会影响 DMSQL 程序的正确执行。
1.4.1 静态游标

静态游标是只读游标,它总是按照打开游标时的原样显示结果集。

  • 隐式游标无需用户进行定义,每当用户在 DMSQL 程序中执行一个 DML 语句(INSERT、UPDATE、DELETE、SELECT)或者 SELECT...INTO 语句时,DMSQL 程序都会自动声明一个隐式游标并管理这个游标。

案例 将孙丽的电话号码修改为 13818882888。示例中的"SQL"为隐式游标的名称。

sql 复制代码
BEGIN
	UPDATE PERSON.PERSON SET PHONE=13818882888 WHERE NAME='孙丽';
	IF SQL%NOTFOUND THEN
		PRINT '此人不存在';
	ELSE
		PRINT '已修改';
	END IF;
END;
/
  • 显式游标指向一个查询语句执行后的结果集区域。当需要处理返回多条记录的查询时,应显式地定义游标以处理结果集的每一行。

使用显式游标一般包括四个步骤:

  1. 定义游标:在 DMSQL 程序的声明部分定义游标,声明游标及其关联的查询语句;
  2. 打开游标:执行游标关联的语句,将查询结果装入游标工作区,将游标定位到结果集的第一行之前;
  3. 拨动游标:根据应用需要将游标位置移动到结果集的合适位置;
  4. 关闭游标:游标使用完后应关闭,以释放其占有的资源。

fetch 选项指定将游标移动到结果集的某个位置:

  • NEXT:游标下移一行
  • PRIOR:游标前移一行
  • FIRST:游标移动到第一行
  • LAST:游标移动到最后一行
  • ABSOLUTE n:游标移动到第 n 行
  • RELATIVE n:游标移动到当前指示行后的第 n 行

例 如何使用不同语法定义各种显式游标。

sql 复制代码
DECLARE
	CURSOR c1 IS SELECT TITLE FROM RESOURCES.EMPLOYEE WHERE MANAGERID = 3;
	CURSOR c2 RETURN RESOURCES.EMPLOYEE%ROWTYPE IS SELECT * FROM
RESOURCES.EMPLOYEE;
	c3 CURSOR IS TABLE RESOURCES.EMPLOYEE;
......

INTO 子句中的变量个数、类型必须与游标关联的查询语句中各 SELECT 项的个数、类型一一对应。典型的使用方式是在 LOOP 循环中使用 FETCH 语句将每一条记录数据赋给变量,并进行处理,使用%FOUND 或 %NOTFOUND来判断是否处理完数据并退出循环。如下例所示:

sql 复制代码
DECLARE 
	v_name VARCHAR(50);
    v_phone VARCHAR(50);
	c1 CURSOR FOR SELECT NAME,PHONE FROM PERSON.PERSON A,RESOURCES.EMPLOYEE B WHERE A.PERSONID=B.PERSONID;   
BEGIN
    OPEN c1; -- 打开游标、游标中开始加载数据
    LOOP
        FETCH c1 INTO v_name, v_phone;
        EXIT WHEN c1 %NOTFOUND; -- 判断是否获取到游标的最后面一行了
        PRINT v_name || v_phone;
    END LOOP;
    CLOSE c1;
END;

使用 FETCH...BULK COLLECTINTO 可以将查询结果批量地、一次性地赋给集合变量。FETCH...BULK COLLECTINTO LIMIT rows 配合使用,可以限制每次获取数据的行数。

sql 复制代码
DECLARE
    -- 1. 自定义一条【记录类型】,相当于一行数据的结构
    TYPE V_rd IS RECORD(V_NAME VARCHAR(50), V_PHONE VARCHAR(50));
    -- 2. 自定义【表类型】,用来存多行上面的记录,用【整数 1、2、3...】当索引(下标),当成数组用
    TYPE V_type IS TABLE OF V_rd INDEX BY INT;
    -- 3. 定义变量,用来真正存数据
    v_info V_type;
	c1 CURSOR IS SELECT NAME,PHONE FROM PERSON.PERSON A,RESOURCES.EMPLOYEE B WHERE A.PERSONID=B.PERSONID;
BEGIN
	OPEN c1;
	FETCH c1 BULK COLLECT INTO v_info; -- 批量获取到的数据批量地、一次性地赋给集合
	CLOSE c1;
	FOR I IN 1..v_info.COUNT LOOP
		PRINT v_info(I).V_NAME ||v_info(I).V_PHONE;
	END LOOP;
END;
/
1.4.2 动态游标

与静态游标不同,动态游标在声明部分只是先声明一个游标类型的变量,并不指定其关联的查询语句,在执行部分打开游标时才指定查询语句

动态游标在 OPEN 时通过 FOR 子句指定与其关联的查询语句。

sql 复制代码
-- 使用动态游标输出员工的姓名、工号和薪水
DECLARE
    my_ename CHAR(10);
    my_empno NUMERIC(4);
    my_sal   NUMERIC(7, 2);
    c1 CURSOR;
BEGIN
    OPEN C1 FOR SELECT * FROM OTHER.EMPSALARY; --在打开的时候才指定查询的语句
    LOOP
        FETCH c1 INTO my_ename, my_empno, my_sal;
        EXIT WHEN c1 %NOTFOUND;
        PRINT '姓名' || my_ename || '工号' || my_empno || ' 薪水' || my_sal;
    END LOOP;
    CLOSE c1;
END;

/
1.4.3 使用静态游标更新、删除数据

可以使用静态游标更新或删除结果集中的数据。若需要使用游标更新或删除数据,则在游标关联的查询语句中一定要使用``FOR UPDATE 选项。FOR UPDATE 选项出现在查询语句中,用于对要修改的行上锁,以防止用户在同一行上进行修改操作

当游标拨动到需要更新或删除的行时,就可以使用 UPDATE/DELETE 语句进行数据更新/删除。此时必须在 UPDATE/DELETE 语句结尾使用"WHERE CURRENT OF 子句",以限定删除/更新游标当前所指的行。

例 1 使用游标更新表中的数据。

sql 复制代码
DECLARE
	CURSOR csr is SELECT SALARY FROM RESOURCES.EMPLOYEE WHERE TITLE='销售经理' FOR UPDATE;
BEGIN
    OPEN csr;
    IF csr %ISOPEN THEN
        FETCH csr;
        UPDATE RESOURCES.EMPLOYEE SET SALARY = SALARY + 10000 WHERE CURRENT OF csr;
    ELSE
        PRINT 'CURSOR IS NOT OPENED';
    END IF;
    CLOSE csr;
END;
/

使用游标删除表中的数据

sql 复制代码
DECLARE
	CURSOR dcsr is SELECT EMPNO FROM OTHER.EMPSALARY WHERE ENAME='KING' FOR UPDATE;
BEGIN
	OPEN dcsr;
	IF dcsr%ISOPEN THEN
		FETCH dcsr;
		DELETE FROM OTHER.EMPSALARY WHERE CURRENT OF dcsr;
	ELSE
		PRINT 'CURSOR IS NOT OPENED';
	END IF;
	CLOSE dcsr;
END;
/

1.5 动态 SQL

静态 SQL 在 DMSQL 程序进行编译时是明确的 SQL 语句,处理的数据库对象也是明确的,这些语句在编译时就可进行语法和语义分析处理。

DMSQL 程序还支持动态 SQL,动态 SQL 指在 DMSQL 程序进行编译时是不确定的 SQL,编译时对动态 SQL 不进行处理,在 DMSQL 程序运行时才动态地生成并执行这些 SQL。

在应用中,常常需要根据用户的选择(如表名、列名、排序方式等)来生成 SQL 语句并执行,这些 SQL 不能在应用开发时确定,此时就需要使用动态 SQL。

sql 复制代码
CREATE OR REPLACE PROCEDURE proc(cate IN INT, time IN DATE)
AS
    DECLARE
    str_sql varchar := 'SELECT NAME,PUBLISHER from PRODUCTION.PRODUCT WHERE PRODUCT_SUBCATEGORYID = ? AND PUBLISHTIME> ?';
BEGIN
    EXECUTE IMMEDIATE str_sql USING cate,time;
EXCEPTION
    WHEN OTHERS THEN
        PRINT 'error';
END;
/ 
CALL proc(4, '2001-01-01');

二、系统表、系统视图

了解系统表、系统视图,以及运维中常用的SQL语句,提到数据库,避免不了数据库优化,发现数据库瓶颈。

常见的、造成数据库性能下降的原因有很多,比如:某个用户请求读取大量数据、某段时间系统的负载压力特别大、业务巅峰超高并发访问,长SQL...

2.0 DM 数据库管理常用 SQL 命令

sql 复制代码
--查询数据库版本
select * from  v$version;--DM7
select id_code();--DM8

--查询授权信息
select * from  v$license;

--查询服务器信息
select * from  V$SYSTEMINFO;

--查询会话连接信息
select * from  v$sessions;

select  count(*),state from v$sessions group by state;
select  count(*),clnt_ip from v$sessions group by clnt_ip;

--查看数据库服务器配置参数

select * from  v$dm_ini;

--查询最近的 sql 执行记录
select * from  v$sql_history;

--查询某个用户下所有的表
select * from  user_tables; --查询当前用户下所有的表

select * from  all_tables where owner='TEST'; --dba 用户查询某个模式下的所有表

--查询某个用户下所有表字段
select * from  all_tab_cols where owner='TEST';

--查看表注释
select * from  ALL_TAB_COMMENTS where owner='TEST';

--查看字段注释
select * from  ALL_COL_COMMENTS where owner='TEST';

select '实例名称' 数据库选项,INSTANCE_NAME 数据库集群相关参数值 FROM v$instance union all

select '数据库版本',substr(svr_version,instr(svr_version,'(')) FROM v$instance union all   SELECT '字符集',CASE SF_GET_UNICODE_FLAG() WHEN '0' THEN 'GBK18030' WHEN '1' then 'UTF-8' when '2' then 'EUC-KR' end union all

SELECT '页大小',cast(PAGE()/1024 as varchar) union all   SELECT '簇大小',cast(SF_GET_EXTENT_SIZE() as varchar) union all

SELECT '大小写敏感',cast(SF_GET_CASE_SENSITIVE_FLAG() as varchar) union all

select '数据库模式',MODE$ from v$instance union all

select '唯一魔数',cast(permanent_magic as varchar) union all

select 'LSN',cast(cur_lsn as varchar) from v$rlog;

-- 查询 SQL 执行记录,QL 执行历史存放数据有限,若有需要,可以开启 DEM 监控
SELECT * FROM V$SQL_HISTORY;

2.1 REDO 日志信息

sql 复制代码
SELECT
    A.FILE_ID,
    A.PATH,
    A.CLIENT_PATH,
    ROUND(A.RLOG_SIZE / 1024 / 1024 / 1024, 2) AS   RLOG_SIZE_GB,
    ROUND(B.FREE_SPACE / 1024 / 1024 / 1024, 2) AS  FREE_SPACE_GB,
    ROUND(B.TOTAL_SPACE / 1024 / 1024 / 1024, 2) AS TOTAL_SPACE_GB,
    B.CUR_FILE
from
    (select * from V$RLOGFILE) A,
    (select * from V$RLOG) B;

修改redo日子的大小

sql 复制代码
ALTER DATABASE RESIZE LOGFILE 'dameng_003.log' to 2048

2.2 表空间大小信息

TEMP 表空间完全由 DM 数据库自动维护。当用户的 SQL 语句需要磁盘空间来完成某个操作时,DM 数据库会从 TEMP 表空间分配临时段。如创建索引、无法在内存中完成的排序操作、SQL 语句中间结果集以及用户创建的临时表等都会使用到 TEMP 表空间。查看方法如下:

sql 复制代码
SELECT
    a.tablespace_name                     "表空间名称",
    total / (1024 * 1024)                 "表空间大小(M)",
    free / (1024 * 1024)                  "表空间剩余大小(M)",
    (total -free) / (1024 * 1024)         "表空间使用大小(M)",
    total / (1024 * 1024 * 1024)          "表空间大小(G)",
    free / (1024 * 1024 * 1024)           "表空间剩余大小(G)",
    (total -free) / (1024 * 1024 * 1024)  "表空间使用大小(G)",
    round((total -free) / total, 4) * 100 "使用率 %"
FROM
    (SELECT tablespace_name, SUM(bytes) free FROM dba_free_space GROUP BY tablespace_name) a,
    (SELECT tablespace_name, SUM(bytes) total FROM dba_data_files GROUP BY tablespace_name) b
WHERE
    a.tablespace_name = b.tablespace_name;

2.3 Undo 回滚段

Undo 回滚段也被称为 undo 表空间,是由 DM 数据库自动维护管理。记录的是数据变动过程中的各个版本信息。在数据库数据发生频繁变动时,会生成大量的回滚段记录,由参数 UNDO_RETENTION 来控制回滚页保持时间,默认为 90s,一般保持默认即可。设置太小,可能会影响数据大批量查询;设置太大,则会在数据库启动时做过多的回滚操作。

Oracle对于这种undo回滚段会出现快照过旧的情况。

2.4 死锁、阻塞、占用资源最多的SQL排查

死锁

如果在结果集中发现有死锁需要及时进行排查并释放锁,避免影响业务正常运行。

sql 复制代码
select * from V$DEADLOCK_HISTORY;

查询慢 SQL 及阻塞

sql 复制代码
--检查当前数据库中包含的慢 SQL 及阻塞语句
SELECT
    DS.SESS_ID     "被阻塞的会话ID",
    DS.SQL_TEXT    "被阻塞的SQL",
    DS.TRX_ID      "被阻塞的事务ID",
    (
     CASE L.LTYPE
         WHEN 'OBJECT' THEN
             '对象锁'
         WHEN 'TID' THEN
             '事务锁'
     END
     CASE)         "被阻塞的锁类型",
    DS.CREATE_TIME "开始阻塞时间",
    SS.SESS_ID     "占用锁的会话ID",
    SS.SQL_TEXT    "占用锁的SQL",
    SS.CLNT_IP     "占用锁的IP",
    L.TID          "占用锁的事务ID"
FROM
    V$LOCK L
    LEFT JOIN V$SESSIONS DS ON DS.TRX_ID = L.TRX_ID
    LEFT JOIN V$SESSIONS SS ON SS.TRX_ID = L.TID
WHERE
    L.BLOCKED = 1;

查询死锁历史事务信息

sql 复制代码
select
    dh.trx_id,
    sh.sess_id,
    wm_concat(top_sql_text)
from
    V$DEADLOCK_HISTORY dh,
    V$SQL_HISTORY sh
where
    dh.trx_id = sh.trx_id
    and dh.sess_id = sh.sess_id
group by
    dh.trx_id,
    sh.sess_id;

查询已执行超过 2 秒的活动 SQL

sql 复制代码
select * 
from (
    SELECT user_name,clnt_ip,sess_id,sql_text,
           datediff(ss, last_send_time, sysdate) ss,
           SF_GET_SESSION_SQL(SESS_ID) fullsql
    FROM V$SESSIONS
    WHERE STATE = 'ACTIVE' and user_name != 'SYSDBA'
) t
where ss >= 2
order by 5 desc;

查询实例中已执行未提交的 SQL

sql 复制代码
SELECT t1.sql_text, t1.state, t1.trx_id
 FROM v$sessions t1, v$trx t2
 WHERE t1.trx_id = t2.id AND t1.state = 'IDLE' AND t2.status = 'ACTIVE';

查询占用内存最多的 sql

sql 复制代码
--查询执行在 1 秒以上的SQL语句使用的内存
SELECT "SESSID", MAX_MEM_USED||'KB',SQL_TXT FROM V$SQL_STAT order by MAX_MEM_USED DESC;

查询占用 io 较大的 sql

sql 复制代码
--查询物理读次数较大的 sql
select
        s.sess_id,
        s.sql_text,
        s.user_name,
        s.state,
        st.PHY_READ_CNT,
        st.iO_WAIT_TIME
from
        v$sessions s, v$session_stat st
where
        s.sess_id=st.sessid and s.state  ='ACTIVE'
ORDER BY  5 DESC;

查找性能相对较差的 sql。 即磁盘读取次数较多,按照硬解析读取次数倒叙排序。

sql 复制代码
select
    SQL_TXT,
    EXEC_TIME,
    PARSE_CNT,
    PARSE_TIME,
    HARD_PARSE_CNT,
    HARD_PARSE_TIME
FROM
    v$sql_stat
ORDER BY
    HARD_PARSE_CNT DESC,
    EXEC_TIME DESC;

2.5 统计信息

操作系统统计信息

包括对象的 id、对象类型、统计对象名、统计值等。

sql 复制代码
select * from V$SYSSTAT where classid in (11,5) order by classid desc;

会话统计信息

通过以下 SQL 命令可以查询到会话信息,包括会话活动状态、客户端 IP、客户端类型、当前模式、当前用户、会话数量等。

sql 复制代码
SELECT STATE,CLNT_IP,CLNT_TYPE,CURR_SCH,USER_NAME,COUNT(*) COUNTS 
FROM V$SESSIONS 
GROUP BY STATE,CLNT_IP,CLNT_TYPE,CURR_SCH,USER_NAME
ORDER BY STATE;

最慢的 20 条 SQL 统计

sql 复制代码
SELECT TOP 20 START_TIME,TIME_USED/1000 TIME_USED,TOP_SQL_TEXT FROM V$SQL_HISTORY ORDER BY TIME_USED DESC;
SELECT * FROM V$SYSTEM_LONG_EXEC_SQLS ORDER BY EXEC_TIME DESC;

高内存的 20 条 SQL 统计

sql 复制代码
select * from  V$SYSTEM_LARGE_MEM_SQLS order by mem_used_by_k desc;

前 20 条长耗时等待事件统计

sql 复制代码
SELECT top 20 * FROM V$SYSTEM_EVENT ORDER BY TOTAL_WAITS DESC;
相关推荐
CQU_JIAKE1 小时前
5.13【A】
数据库·sql
ziqi5221 小时前
Docker compose 和共享数据
运维·docker·容器
lzhdim1 小时前
SQL 入门 14:SQL 触发器与事件:自动化数据处理
linux·前端·数据库·sql·自动化
环流_2 小时前
redis中hash的应用场景
数据库·redis·哈希算法
@我漫长的孤独流浪2 小时前
医院病房管理系统E-R建模与关系转换
数据库
_codemonster2 小时前
系统分析师系列目录
java·网络·数据库
|_⊙2 小时前
Linux 深入理解文件(Ext2文件系统:下)
linux·服务器·数据库
袁小皮皮不皮2 小时前
HCIP-BFD 学习笔记
运维·服务器·网络·笔记·网络协议·学习·智能路由器
恋奴娇2 小时前
ubuntu 25 gnome-screenshot 录屏启动失败 原因pipewire服务未启动
linux·运维·ubuntu