数据库整理(五)数据库编程
嵌入式SQL
交互式SQL和嵌入式SQL是SQL的两种使用方法。将SQL语言嵌入到宿主语言程序中,将SQL语言访问数据库的功能和宿主语言的数据处理功能相结合,这种使用SQL的方法称为嵌入式SQL。
- SQL查询数据库的能力强,高级语言对复杂应用和复杂逻辑处理能力强
- 类似断言、触发器等操作需要对数据进行处理,原生SQL能力不强
- 数据库服务器只识别SQL语句,用SQL可以提高效率
如何实现嵌入式SQL?
- 宿主语言(简称主语言)的编译器不能识别SQL,如何将嵌有SQL语句的宿主语言编译成可执行代码?
- 宿主语言与DBMS之间如何传递数据和信息?
- 如何解决元组集合数据对宿主语言变量的赋值?
识别与处理SQL
数据库管理系统一般采用预编译方法处理,即由数据库管理系统的预处理程序对源程序进行扫描,识别出嵌入式SQL语句,把它们转换成主语言调用语句,以使主语言编译程序能识别它们,然后由主语言的编译程序将纯的主语言程序编译成目标码
为了调用,将SQL语言转变为函数调用
用EXEC SQL前缀来标识SQL语句,以区分宿主语言与SQL语言。
EXEC SQL <SQL语句>;
嵌入式SQL语句与主语言的通信
SQL负责操纵数据库,高级语言语句负责控制逻辑流程
数据库工作单元与源程序工作单元之间的通信主要包括:
- (1)向主语言传递SOL语句的执行状态信息,使主语言能据此信息控制程序流程。主要SQL通信区(SQLCA)实现
- (2)主语言向SQL语句提供参数,主要用主变量( host variable)实现
- (3)将SOL语句查询数据库的结果交主语言处理,主要用主变量和游标( cursor)实现。
SQL通信区
描述当前的工作状态与运行环境 是一个数据结构 应用程序每执行完一条SQL语句之后都应该测试一下 SQLCODE的值,以了解该SQL语句执行情况并做相应处理
定义SQLCA:
Exec SQL Include SQLCA
- 使用SQLCA:变量SQLCODE,为0-执行成功,否则表示出错。
主变量
SQL语言中使用的主语言程序变量简称为主变量
宿主语言与SQL语言之间通过共享变量进行数据传送
共享变量由宿主语言程序定义,可在SQL语句中引用。
输入主变量由应用程序对其赋值,SQL语句引用:输出主变量由SQL语句对其赋值或设置状态信息,返回应用程序。
所有主变量和指示变量必须在SQL语句
BEGIN DECLARE SECTION
与END DECLARE SECTION
之间进行说明。说明之后,主变量可以在SQL语句中任何一个能够使用表达式的地方出现。为了与数据库对象名(表名、视图名、列名等)区别,SQL语句中的主变量名和指示变量前要加冒号(:)作为标志。查询结果为单个记录的,可以使用INTO子句指定存放查询结果的主变量
EXEC SQL SELECT Sno, Sname, Ssex, Sage, Sdept INTO Hsno, Hname, Hsex, Hage, Dept FROM Student WHERE Sno= givens;/把要查询的学生的学号赋给了主变量 givens
游标
SQL是面向集合的,一条SQL语句可以产生或处理多条记录:而主语言是面向记录的,一组主变量一次只能存放一条记录。所以仅使用主变量并不能完全满足SQL语句向应用程序输出数据的要求。
游标是系统为用户开设的一个数据缓冲区,存放SQL语句的执行结果,每个游标区都一个名字。用户可以通过游标逐一获取记录并赋给主变量,交由主语言进一步处理
超过一行结果用游标,一行结果交给主变量
采用类似指针的技术定位某一个元组
定义游标:Exec SQL declare cursor for ; (说明性语句 实际并不执行查询子句)
打开游标:Exec SQL open ; (执行查询语句,将查询结果放到缓冲区,指针指向第一条记录)
推进游标:Exec SQL fetch into ;
关闭游标:Exec SQL close ;
数据库连接与关闭
建立连接的嵌入式SQL语句是 EXEC SQL CONNECT TO target[AS connection-namel【USER user-name]; target是要连接的数据库服务器 Connection-name是可选的连接名 关闭数据库连接的嵌入式SQL语句是 EXEC SQL DISCONNECT [connection]; 其中, connection是 EXEC SQL CONNECT所建立的数据库连接。
实例:(嵌入式SQL有声明【主变量、游标等】和执行两大部分)
EXEC SQL BEGIN DEC LARE SECTION; /*主变量说明开始*/ char deptname[64]; char HSno[64]; char HSname[64]; char HSsex[64]; int HSage; int NEWAGE; EXEC SQL END DECLARE SECTION; /*主变量说明结束*/ long SQLCODE; EXEC SQL INCLUDE sqlca; /*定义SQL通信区*/ int main(void) /*C语言主程序开始*/ { int count = 0; char yn; /*变量yn代表yes或no*/ printf("Please choose the department name(CS/MA/IS): "); scanf("%s", deptname); /*为主变量deptname赋值*/ EXEC SQL CONNECT TO TEST@localhost:54321 USER "SYSTEM" /"MANAGER"; /*连接数据库TEST*/ EXEC SQL DECLARE SX CURSOR FOR /*定义游标SX*/ SELECT Sno, Sname, Ssex, Sage /*SX对应语句的执行结果*/ FROM Student WHERE SDept = :deptname; EXEC SQL OPEN SX; /*打开游标SX便指向查询结果的第一行*/ for ( ; ; ) /*用循环结构逐条处理结果集中的记录*/ { EXEC SQL FETCH SX INTO :HSno, :HSname, :HSsex,:HSage; /*推进游标,将当前数据放入主变量*/ if (sqlca.sqlcode != 0) /* sqlcode != 0,表示操作不成功*/ break; /*利用SQLCA中的状态信息决定何时退出循环*/ if(count++ == 0) /*如果是第一行的话,先打出行头*/ printf("\n%-10s %-20s %-10s %-10s\n", "Sno", "Sname", "Ssex", "Sage"); printf("%-10s %-20s %-10s %-10d\n", HSno, HSname, HSsex, HSage); /*打印查询结果*/ printf("UPDATE AGE(y/n)?"); /*询问用户是否要更新该学生的年龄*/ do { scanf("%c",&yn); } while(yn != 'N' && yn != 'n' && yn != 'Y' && yn != 'y'); if (yn == 'y' || yn == 'Y') /*如果选择更新操作*/ { printf("INPUT NEW AGE:"); scanf("%d",&NEWAGE); /*用户输入新年龄到主变量中*/ EXEC SQL UPDATE Student /*嵌入式SQL*/ SET Sage = :NEWAGE WHERE CURRENT OF SX ; } /*对当前游标指向的学生年龄进行更新*/ } EXEC SQL CLOSE SX; /*关闭游标SX不再和查询结果对应*/ EXEC SQL COMMIT WORK; /*提交更新*/ EXEC SQL DISCONNECT TEST; /*断开数据库连接*/ }
过程化SQL
- SQL是非过程化数据库语言,过程化SQL是标准SQL的扩充 使SQL语言能兼顾数据访问和处理能力
- SQL的过程化扩充使SQL编程成为可能,使SQL语言能兼顾数据访问和处理能力
- 过程化SQL程序的基本结构是块。所有的过程化SQL程序都是由块组成的。这些块之间可以互相嵌套,每个块完成一个逻辑操作。
- Oracle使用PL/SQL 可使用循环、分支和嵌套 集成在数据库中,调用更快 减少了网络的交互,有助于提高程序性能。
PL/SQL程序的基本结构
DECLARE—可选部分 变量、常量、游标、用户定义异常的声明 …… BEGIN—必要部分 SQL语句和PL/SQL语句构成的执行程序 …… EXCEPTION—可选部分 程序出现异常时,捕捉异常并处理异常 …… END;—必须部分
触发器( trigger)
- 触发器是一系列SQL语句构成的过程化程序体,对数据库做修改(包括插入、删除和更新)时,它自动被系统执行。
- 触发器是用户定义在关系表上的一类由事件驱动的特殊过程。
- 一旦定义,触发器将被保存在数据库服务器中。任何用户对表的增、删、改操作均由服务器自动激活相应的触发器,在关系数据库管理系统核心层进行集中的完整性控制。
- 触发器类似于约束但是比约束更加灵活,可以实施更为复杂的检查和操作,具有更精细和更强大的数据控制
- 触发器又叫做事件一条件一动作规则。当特定的系统事件发生时,对规则的条件进行检查,如果条件成立则执行规则中的动作,否则不执行该动作。
设计触发器的两个条件
- 指明什么条件下触发器被执行,即触发条件;
- 指明触发器执行的动作是什么,即触发什么。
定义触发器
CREATE TRIGGER <触发器名> { BEFORE | AFTER} <触发事件> ON <表名> FOR EACH {ROW | STATEMENT} <触发动作体>;
- 只有表的拥有者,即创建表的用户才可以在表上创建触发器
- 同一模式下,触发器名必须是唯一的,并且触发器名和表名必须在同一模式下。
- 触发器只能定义在基本表上,不能定义在视图上。当基本表的数据发生变化时,将激活定义在该表上相应触发事件的触发器,因此该表也称为触发器的目标表。
- 触发事件触发事件可以是 INSERT、 DELETE或 UPDATE,也可以是这几个事件的组合。 AFTER/ BEFORE是触发的时机。 AFTER表示在触发事件的操作执行之后激活触发器:BEFORE表示在触发事件的操作执行之前激活触发器
- After是进行后续处理的(比如已经删除列之后),Before是为了完整性(例如不让删除列)
- 触发器类型触发器按照所触发动作的间隔尺寸可以分为行级触发器(FOR EACH ROW)和语句级触发器 (STATEMENT)
- 假设表 TEACHER有1000行,如果定义的触发器为语句级触发器,那么执行完UPDATE语句后触发动作体执行一次;如果是行级触发器,触发动作体将执行1000次。
- 如果是行级触发器,用户可以在过程体中使用NEW和OLD引用 UPDATE/INSERT事件之后的新值和 UPDATE/DELETE事件之前的旧值:如果是语句级触发器,则不能在触发动作体中使用NEW或OLD进行引用
- 如果触发动作体执行失败,激活触发器的事件(即对数据库的增、删、改操作)就会终止执行,触发器的目标表或触发器可能影响的其他对象不发生任何变化
定义一个BEFORE行级触发器,为教师表Teacher定义完整性规则“讲师的工资不得低于3000元,如果低于3000元,自动改为3000元”。 CREATE TRIGGER Insert_Or_Update_Sal BEFORE INSERT OR UPDATE ON Teacher /*触发事件是插入或更新操作*/ FOR EACH ROW /*行级触发器*/ BEGIN /*定义触发动作体,是PL/SQL过程块*/ IF (new.Job=‘讲师') AND (new.Sal < 3000) THEN new.Sal :=3000; END IF; END;
激活触发器
触发器的执行,是由触发事件激活的,并由数据库服务器自动执行。
一个数据表上可能定义了多个触发器。
同一个表上的多个触发器激活时遵循如下的执行顺序:
1. 执行该表上的BEFORE触发器;
2. 激活触发器的SQL语句;
3. 执行该表上的AFTER触发器;
(多个BEFORE执行顺序不相同)
删除触发器
触发器必须是一个已经创建的触发器,并且只能由具有相应权限的用户删除。
DROP TRIGGER <触发器名> ON <表名>; 如: DROP TRIGGER Insert_Sal ON Teacher;
触发器的利弊
- 可以进行更为复杂的检查和操作,具有更精细和更强大的数据控制能力,能够保证数据库的一致性
- 检测和维护触发器需要很大的开销,降低了数据库增、删、改的效率
函数与存储过程
函数与存储过程的区别是:函数必须指定返回的类型
过程化SQL块主要有两种类型,即命名块和匿名块。
- 匿名块每次执行时都要进行编译,它不能被存储到数据库中,也不能在其他过程化SQL块中调用。
- 过程和函数是命名块,它们被编译后保存在数据库中,称为持久性存储模块,可以被反复调用,运行速度较快
存储过程:由过程化SQL编写的过程,经编译和优化后存储在数据库服务器中,使用时只要调用即可。
存储过程降低了客户机和服务器之间的通信量。客户机上的应用程序只要通过网络向服务器发出调用存储过程的名字和参数,就可以让关系数据库管理系统执行其中的多条SQL语句并进行数据处理。只有最终的处理结果才返回客户端
创建存储过程与函数
/*创建存储过程*/ Create [or replace] procedure 过程名 [(argument1 [in|out|in out] type1 , argument2 [in|out|in out] type2, ……)] {IS | AS} <类型、变量的说明> Begin <执行部分> Exception <可选的异常处理部分> end; /*创建函数*/ Create or replace function 函数名 [(argment [{in|out|in out}] type,…)] Return return_type {IS |AS} <类型、变量说明> Begin <函数体,执行部分> Exception <可选的异常处理部分> End;
示例:
/*修改成绩*/ create or replace procedure set_grade(sid sc.sno%type, cid sc.cno%type, newgrade sc.grade%type, st out varchar2) is begin update sc set grade=newgrade where sno=sid and cno=cid; st:='OK!'; commit; exception when no_data_found then st:='数据不存在!'; when others then st:='操作失败!'; end; /*调用*/ declare mm varchar2(20); begin set_grade('20140001','1001',90,mm); dbms_output.put_line('操作结果:' || mm); end;
事务
事务是一系列的数据库操作,是数据库应用程序的基本逻辑单元。
不可分割的工作单位,恢复与并发控制的基本单位
一个事务就是将一系列的数据操纵SQL语句作为一个逻辑单元,逻辑单元里面的单个操作要么全做,要么全部不做,以保证数据的完整性。
一个事务可以是一条SQL语句,一组SQL语句或整个程序。一般来讲,一个程序包含多个事务。
事务的开始与结束可以用户显式的控制。不显式的定义,系统会自动划分。
BEGIN TRANSACTION: 事务开始 COMMIT; 提交事务的所有操作,事务正常结束 ROLLBACK;事务回滚,运行过程中对数据库的所有操作全部撤销,回滚到事务开始的状态
事务的特性
原子性
不可分割 所有操作要么都做 要么不做
一致性
事务执行的结果必须是从一个一致性状态到另一个一致性状态 操作要么全做 要么不做 数据库都处于一致的状态
隔离性
一个事务的执行不能被其他事务干扰,并发执行的各个事务不能互相干扰 锁技术
持续性
永久性 事务一旦提交,对数据库中数据的改变是永久性的。