背景
标准SQL缺少流程控制能力、难以实现应用业务中的逻辑控制
方式
嵌入式SQL(Embedded SQL,ESQL)
过程化SQL(Procedural Language/SQL,PL/SQL)
存储过程和自定义函数
开放数据库互连(Open Data Base Connectivity,ODBC)
OLE DB(Object Linking and Embedding DB)
Java数据库连接(Java Data Base Connectivity,JDBC)
嵌入式SQL
概念
将SQL语句嵌入程序设计语言中,借助高级语言的控制功能实现过程化。
被嵌入的程序设计语言称为宿主语言,简称主语言。
调用过程
数据库管理系统采用预编译方法处理,即由DBMS的预处理程序对源程序进行扫描,识别出嵌入式SQL语句,把它们转换成主语言调用语句,以使主语言编译程序能识别它们,然后由主语言的编译程序将纯的主语言编译成目标码。
嵌入式SQL与主语言之间的通信
功能与相应的实现模块
① 向主语言传递SQL语句的执行状态信息,使主语言能够据此信息控制程序流程。由SQL通信区(SQL Communication Area,SQLCA)实现。
② 主语言向SQL提供参数。由主变量(host variable)实现。
③ 将SQL语句执行数据库的结果交给主语言处理。由主变量和游标(cursor)实现。
SQLCA
存放SQL语句执行后系统要反馈给应用程序的若干信息。包括描述系统当前工作状态和运行环境的工作数据。
SQLCA中由一个变量SQLCODE,用来存放SQL语句执行后返回的代码。SUCCESS/
主变量
嵌入式SQL语句中使用主程序的语言变量来输入或输出数据,称为主变量。
SQL中的主语言的变量名要加:
指示变量
一个主变量可以附带一个任选的指示变量。
指示变量是一个整型变量,用来指示所指主变量的值或条件。
SQL中的指示变量前要加:
指示变量与查询:查出来的值为空值,则相应主变量的指示变量为负值
指示变量与增删改:在应该输入主变量的位置处填入之前已经赋过值的一个变量作为指示变量,则增删改会往对应的值处填入空值
游标
主语言是面向记录的,一组主变量一次只能存放一条记录
SQL语句是面向集合的,一条SQL语句可以产生或处理多条记录
游标是系统为用户开设的一个数据缓冲区,存放SQL的执行结果,每个游标区都有一个名字
游标在SELECT语句中的使用
说明游标
EXEC SQL DECLARE <游标名> CURSOR FOR <SELECT语句>;
打开游标
EXEC SQL OPEN <游标名>;
推进游标指针并取当前记录
EXEC SQL FETCH <游标名>
INTO <主变量>[<指示变量>][,<主变量>[指示变量]]...;
关闭游标
EXEC SQL CLOSE <游标名>;
游标在增删改上的使用
先使用带游标的SELECT语句查出所有记录,然后使用逻辑运算找到要进行修改的记录,使用WHERE CURRENT OF <游标名>来更改当前选中的记录。
EXEC SQL UPDATE Student SET Sage=:NEWAGE WHERE CURRENT OF SX;
动态SQL
普通语句动态SQL
使用SQL语句主变量(即原本声明主变量的地方声明char*类型的字符串,字符串内容为SQL语句)
EXEC SQL BEGIN DECLARE SECTION;
const char* stmt="CREATE TABLE test(a int);";
EXEC SQL END DECLARE SECTION;
执行SQL语句
EXEC SQL EXECUTE IMMEDIATE :stmt;
需输入参数的动态SQL
使用SQL语句主变量
EXEC SQL BEGIN DECLARE SECTION;
const char* stmt="INSERT INTO TEST VALUE (?);";
EXEC SQL END DECLARE SECTION;
在SQL语句主变量中使用(?)表示该位置的数据在运行时确定
EXEC SQL PREPARE mystmt FROM :stmt;
执行SQL语句
EXEC SQL EXECUTE mystmt USING 100;
过程化SQL
概念
过程化SQL是对SQL的扩展,使其增加了过程化语句功能。
过程化SQL程序的基本结构是块。
所有的过程化SQL程序都是由块组成。
块之间可以嵌套,每个块完成一个逻辑操作。
过程化SQL块
结构
使用
变量定义
变量名 数据类型 [[NOT NULL] := 初值表达式]
或
变量名 数据类型 [[NOT NULL] 初值表达式]
常量定义
常量名 数据类型 CONSTANT := 常量表达式
赋值语句
变量名 := 表达式
流程控制之IF-THEN-ELSE-END IF;(可嵌套)
IF condition THEN
Sequence_of_statements1;
ELSE
Sequence_of_statements2;
END IF;
流程控制语句之LOOP
LOOP
Sequence_of_statements;
END LOOP;
支持EXIT、BREAK、LEAVE等结束循环语句
流程控制语句之WHILE-LOOP
WHILE condition LOOP
Sequence_of_statements;
END LOOP;
支持EXIT、BREAK、LEAVE等结束循环语句
流程控制语句之FOR-LOOP
FOR count IN [REVERSE] bound1 .. bound2 LOOP
Sequence_of_statements;
END LOOP;
支持EXIT、BREAK、LEAVE等结束循环语句
执行过程:将count设置为下界bound1,执行语句,然后count+1...(REVERSE是将count设置为上界bound1,执行语句,然后count-1)
错误处理
需根据具体RDBMS的支持情况来进行错误处理
存储过程
概念
存储过程是由过程化SQL语句书写的过程,这个过程经变异和优化后存储在数据库服务器中,使用时只需调用即可
优点
不用在执行时才进行语法分析和优化工作,执行效率高。
降低了客户机和服务器的通信量
方便实施企业规则
创建
CREATE OR REPLACE PROCEDURE 过程名([参数1,参数2,...])
AS <过程化SQL块>;
注:
--or replace代表创建该存储过程时,若存储名存在,则替换原存储过程,重新创建
--无参数列表时,不需要写()
使用
CALL/PERFORM PROCEDURE 过程名([参数1,参数2,...]);
修改
重命名:ALTER PROCEDURE 过程名1 RENAME TO 过程名2;
重编译:ALTER PROCEDURE 过程名 COMPILE;
删除
DROP PROCEDURE 过程名();
例子
创建
1 /* 2 * 定义存储过程TRANSFER,其参数为转入账户、转出账户、转账额度 3 */ 4 CREATE OR REPLACE PROCEDURE TRANSFER (inAccount INT, outAccount INT, amount FLOAT) 5 AS 6 /*定义变量*/ 7 DECLARE 8 totalDepositOut Float; 9 totalDepositIn Float; 10 inAccountNum INT; 11 BEGIN 12 /*检查转出账户的余额*/ 13 SELECT Total INTO totalDepositOut FROM Account WHERE accountnum=outAccount; 14 /*如果转出账户不存在,回滚*/ 15 IF totalDepositOut IS NULL THEN 16 ROLLBACK; 17 RETURN; 18 END IF; 19 /*如果转出账户余额不足,回滚*/ 20 IF totalDepositOut<amount THEN 21 ROLLBACK; 22 RETURN; 23 END IF; 24 /*如果转入账户不存在,回滚*/ 25 SELECT Accountnum INTO inAccountNum FROM Account WHERE accountnum=inAccount; 26 IF inAccountNum IS NULL THEN 27 ROLLBACK; 28 RETURN; 29 END IF; 30 /*转账并提交转账事务*/ 31 UPDATE Account SET total=total-amount WHERE accountnum=outAccount; 32 UPDATE Account SET total=total+amount WHERE accountnum=inAccount; 33 COMMIT; 34 END;
1 create procedure getTotalByUser2( 2 in userId int, 3 in falg boolean, -- 是否加税标记 4 out total decimal(8,2) 5 ) 6 begin 7 DECLARE tmptotal DECIMAL(8,2); 8 DECLARE taxrate int DEFAULT 6;-- 默认的加税的利率 9 10 select SUM(r.price) from order r 11 where r.u_id = userId 12 into tmptotal; 13 14 if taxable then 15 select tmptotal + (tmptotal/1000*taxrate) into tmptotal; 16 end if; 17 18 select tmptotal into total; 19 END;
1 delimiter $ 2 create PROCEDURE phoneDeal() 3 BEGIN 4 DECLARE id varchar(64); -- id 5 DECLARE phone1 varchar(16); -- phone 6 DECLARE password1 varchar(32); -- 密码 7 DECLARE name1 varchar(64); -- id 8 -- 遍历数据结束标志 9 DECLARE done INT DEFAULT FALSE; 10 -- 游标 11 DECLARE cur_account CURSOR FOR select phone,password,name from account_temp; 12 -- 将结束标志绑定到游标 13 DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; 14 15 -- 打开游标 16 OPEN cur_account; 17 -- 遍历 18 read_loop: LOOP 19 -- 取值 取多个字段 20 FETCH NEXT from cur_account INTO phone1,password1,name1; 21 IF done THEN 22 LEAVE read_loop; 23 END IF; 24 25 -- 你自己想做的操作 26 insert into account(id,phone,password,name) value(UUID(),phone1,password1,CONCAT(name1,'的家长')); 27 END LOOP; 28 29 30 CLOSE cur_account; 31 END $
理解
调用
ORACLE --- CALL PROCEDURE TRANSFER(01003815868,01003813828,10000);
MYSQL --- CALL TRANSFER(01003815868,01003813828,10000);
函数
函数与存储过程类似,都是持久性存储模块。
不同:
Oracle 存储过程(procedure)和函数(Function)的区别
创建
CREATE OR REPLACE FUNCTION 函数名 ([参数1,参数2,...]) RETURN <类型>
AS <过程化SQL块>;
执行
CALL/SELECT 函数名 ([参数1,参数2,...]);
修改
重命名:ALTER FUNCTION 函数名1 RENAME TO 函数名2;
重编译:ALTER FUNCTION 函数名 COMPILE;
游标
使用方法同嵌入式SQL的游标使用。