330 likes | 429 Views
PL/SQL. PL/SQL. PL/SQL:Procedural Language/SQL 目的:弥补交互式 SQL 的不足,支持过程化 应用: 编写存储过程( Procedure ) 存储过程由用户、应用程序或触发器显式调用 编写触发器( Trigger ) 触发器则是当触发事件发生时由系统触发执行. PL/SQL 的块结构. 定义部分 DECLARE …… 变量,常量 游标 异常等 执行部分 BEGIN …… SQL 语句、流程控制语句 EXCEPTION …… 异常处理部分 END;. 例题 1.
E N D
PL/SQL PL/SQL:Procedural Language/SQL 目的:弥补交互式SQL的不足,支持过程化 应用: 编写存储过程(Procedure) 存储过程由用户、应用程序或触发器显式调用 编写触发器(Trigger) 触发器则是当触发事件发生时由系统触发执行
PL/SQL的块结构 定义部分 DECLARE ……变量,常量 游标 异常等 执行部分 BEGIN ……SQL 语句、流程控制语句 EXCEPTION ……异常处理部分 END;
例题1 向关系Student中插入10个学生,学号从1001-1010 DECLARE sno INT;
例题1(续) BEGIN sno:=1001; WHILE sno<1011 LOOP Insert into Student(Sno) Values(to_char(sno,’9999’)); sno:=sno+1; END LOOP; END;
PL/SQL 基本语法结构(1) • 说明:以KingbaseES4.0为例 • 变量、常量定义 • identifier [CONSTANT] datatype [NOT NULL] [:= | DEFAULT expr]; • 举例 Declare birthdayDATE; age int NOT NULL := 27; name VARCHAR (13) := 'Levi'; magic CONSTANT int := 77;--常量 valid BOOLEAN NOT NULL := TRUE; • 注意事项 • 定义的常量必须有初值,且不能改变 • 变量缺省初值为NULL • 不支持列表形式的定义 age, empno SMALLINT;
PL/SQL 基本语法结构(2) • 表达式注意事项 • 连接字符 'Renmin' || 'University‘ • KingbaseES的存储过程在处理空值时遵循如下规则: • 涉及空值的比较结果为FALSE; • 涉及空值的加减乘除运算结果为NULL; • 举例 a := 100; b := NULL; IF a != b THEN < SequenceOfStatements >;--会执行吗?? END IF;
PL/SQL 基本语法结构(3) • 控制结构 • 条件语句 IF < ConditionExpression > THEN SequenceOfStatements1 > END IF; IF < ConditionExpression > THEN SequenceOfStatements1 > ELSE < SequenceOfStatements2 > END IF; • 只有在条件为假或NULL时,ELSE后的语句序列才会被执行
PL/SQL 基本语法结构(4) 例如:检查学生是否在注册表中,若在,则登陆次数增加一次,否则把该学生插入注册表中 create table mylog( who varchar (30), logon_num int );
PL/SQL 基本语法结构(5) DECLARE cnt int; user VARCHAR(20) := 'Levi'; BEGIN select count(*) into cnt from mylog where who = user; if cnt > 0 then update mylog set logon_num = logon_num + 1 where who = user; else insert into mylog values(user, 1); end if; end;
PL/SQL 基本语法结构(6) create table number_table( num int ); • 循环结构 • 简单的Loop • While-Loop • For-Loop • 退出语句(与循环结构搭配) • Exit --强制无条件退出循环 • Exit-When --满足条件退出循环 • 举例
PL/SQL 基本语法结构(7) • 举例: 简单Loop • Create or replaceprocedure loop1() as DECLARE i int := 1; BEGIN LOOP INSERT INTO number_table VALUES(i); i := i + 1; EXIT WHEN i > 10; END LOOP; END; • Perform procedure loop1();
PL/SQL 基本语法结构(8) • 举例: While-Loop • Create or replace procedure loop2() as DECLARE TEN int:=10; i int :=1; BEGIN WHILE i <= TEN LOOP INSERT INTO number_table VALUES(i); i := i + 1; END LOOP; END; • Perform procedure loop2();
PL/SQL 基本语法结构(9) • 举例: For-Loop • Create or replace procedureloop3() as DECLARE i int; BEGIN FOR i IN 1..10 LOOP INSERT INTO number_table VALUES(i); END LOOP; END; • Perform procedure loop3();
PL/SQL 基本语法结构(10) • 异常处理 • RAISE { NOTICE | EXCEPTION } FormatString [, SimpleVariableName [, ... n]]; • 举例: • CREATE TABLE testRaise(i INT); • CREATE PROCEDURE TestRaise(i INT) AS BEGIN INSERT INTO testRaise VALUES(i); IF 0 < i AND i < 10 THEN --下面的%指代变量i RAISE NOTICE 'your input value "%" is valid.',i; ELSE RAISE EXCEPTION 'your input value "%" is invalid.',i; END IF; INSERT INTO testRaise VALUES(i); END;
PL/SQL 基本语法结构(11) • 游标的使用:类似与ESQL中的定义和使用 • CREATE TABLE emp(emp_no CHAR(10), salary INT); • CREATE PROCEDURE proc_open() AS DECLARE Para1 CHAR(10); Para2 INT; CURSOR mycursorFOR --后面紧跟SQL语句 SELECT emp_no, salary FROM emp; BEGIN OPEN mycursor; LOOP FETCH mycursor INTO para1,para2; EXIT WHEN mycursor%NOTFOUND; END LOOP; CLOSE mycursor; END;
PL/SQL 基本语法结构(12) • KingbaseES中游标的状态判断 • CursorName%ISOPEN • 获取游标的属性,查看游标是否打开。 • CursorName%NOTFOUND • 获取游标的属性,对于声明的游标,查看上次fetch语句是否取到数据。 • 存储 过程中调用其它存储过程 • PERFORM ProcedureName( < ExpressionList >);
综合例子:把一个表中的数据复制到另一个表中综合例子:把一个表中的数据复制到另一个表中 CREATE PROCEDURE proc_isopen() AS DECLARE c_no CHAR(4); c_name CHAR(20); CURSOR mycursor FOR SELECT lno,lname FROM leader; BEGIN OPEN mycursor; IF mycursor%ISOPEN THEN LOOP FETCH mycursor INTO c_no,c_name; EXIT WHEN (mycursor%NOTFOUND); INSERT INTO temp vALUES(c_no,c_name); END LOOP; END IF; CLOSE mycursor; END;
如何在存储过程中应用PL/SQL 创建Procedure: CREATE [ OR REPLACE ] PROCEDURE ProcedureName ( [< ParameterList >[, ...n]] )AS [DECLARE [< VariableDeclaration >] [< CursorDeclaration >]] BEGIN < SequenceOfStatements >END; 执行Procedure: PERFORM PROCEDURE ProcedureName( [< ExpressionList >] );
例1(续) 创建存储过程 CREATE OR REPLACE PROCEDURE Insertsno() AS DECLARE snum INT; BEGIN snum:=1001; WHILE snum<1011 LOOP Insert into Student(Sno) Values(snum); snum:=snum+1; END LOOP; END; 执行存储过程 PERFORM PROCEDURE Insertsno() ;
存储过程的优点 • 执行效率高 • 传统SQL解释执行,而存储过程经编译和优化后存储在服务器中。因此提供了在服务器端快速执行SQL的有效途径。 • 降低客户端和服务器的网络流量 • 方便实施企业规则。
存储过程的定义(1) • KingbaseES 中的定义形式 CREATE [ OR REPLACE ] PROCEDURE ProcedureName ( [< ParameterList >[, ...n]] ) AS [DECLARE [< VariableDeclaration >] [< CursorDeclaration >] ] BEGIN < SequenceOfStatements > END;
存储过程的定义(2) • 参数定义 {[IN | OUT | INOUT] ParameterName < DataType >} [, ...n] • 使用说明: IN,OUT,INOUT参数模式,定义了形式参数的行为。IN将值传给被调用的子程序; OUT参数将返回值传给调用者; INOUT参数将初始值传给被调用的子程序,并将更新后的值返回给调用者; 缺省为IN类型。注意,OUT 参数在存储过程执行开始时,自动初始化为NULL值,所以OUT参数参与存储过程的运算,首先需要赋值。
存储过程的定义(4) 执行Procedure: PERFORM PROCEDURE ProcedureName( [< ExpressionList >] ); 删除 procedure: Drop PROCEDURE ProcedureName( [< ExpressionList >] );
创建触发器(1) • CREATE TRIGGER TriggerName { BEFORE | AFTER } <DmlEventClause > { < ExecuteProcedureStatement> | AS < PlsqlBlock > }; • 意义: • BEFORE 表示在触发事件之前执行触发器。 • AFTER 表示在触发事件之后执行触发器. • DmlEventClause 表示触发事件,即激活触发器的DML语句。 • < DmlEventClause > ::={ DELETE | INSERT | UPDATE } [OR ...n]ON TableName FOR EACH { ROW | STATEMENT }
Trigger的作用和意义 • 提供保证数据完整性的方法 • 维护数据完整性 • 提高共享度,提高效率 • 把多个应用程序访问数据库遵循的规则通过触发器实现,便于推行规则的实施
操作 new表 old表 Insert 存放新插入的新行 delete 存放被插入的行 Update 存放要更新的新行 存放更新前的行 Trigger中的临时专用表 • DBMS为每个触发器建立两个临时专用表 • Kingbase: new表和old表 • SQLServer:inserted表和deleted表
Trigger中的after和before • S(sno,sname,sage,scsum)、SC(sno,sco,grade)、C(cno,cname) • 例1:根据学生选课信息自动修改S表中的scnum • 例2:插入选课记录前检查该学生是否在S的sno表中
创建触发器说明(2) • 说明: • a. DELETE 当从TableName中删除记录时激活触发器。 • b. INSERT 当向TableName中插入记录时激活触发器。 • c. UPDATE 当TableName中的任意column值发生改变时激活触发器。 • d. OR 在指定对象上设置多个激活事件。
创建触发器说明(2) • TableName 确定创建的触发器所在的表名。 • FOR EACH ROW 表示触发器为行级触发器,即对于触发语句影响到的每条记录都执行一次触发器动作。 • FOR EACH STATEMENT 表示触发器为语句级触发器,即只要相关触发语句执行便激活触发器进行执行。 • 执行存储过程语句 PERFORM PROCEDURE ProcedureName ()
触发器举例(1) • CREATE TABLE tab1(col int); CREATE TABLE tab2(col int); INSERT INTO tab1 VALUES(10); INSERT INTO tab1 VALUES(20); • 行级触发 • CREATE TRIGGER insert_salary_changes BEFORE UPDATE ON tab1 FOR EACH ROW AS DECLARE valuediff INT; BEGIN valuediff := new.col - old.col; INSERT INTO tab2 VALUES(valuediff); END; • 执行:UPDATE tab1 SET col = 100;
触发器举例(2) • CREATE TABLE tab1(col int); CREATE TABLE tab2(col char(15)); INSERT INTO tab1 VALUES(10); INSERT INTO tab1 VALUES(20); • CREATE PROCEDURE InsertInTab() AS BEGIN INSERT INTO tab2 VALUES('tab1 deleted!'); END; • CREATE TRIGGER MyTrigger1 AFTER DELETE ON tab1 FOR EACH ROW EXECUTE PROCEDURE InsertInTab(); • 执行 delete from tab1;结果如何?
触发器举例(3) • 语句级触发 CREATE TRIGGER MyTrigger1 AFTER DELETE ON tab1 FOR EACH STATEMENT EXECUTE PROCEDURE InsertInTab(); • 执行 delete from tab1;结合例子2,结果如何?