第九讲 嵌入式SQL语言之基本技巧
嵌入式SQL语言
为什么需要嵌入式SQL语言?
交互式SQL本身不够灵活, 对于普通用户来说, 复杂的查询掌握起来太难, 开发应用程序很有必要. 而且查询本身具有复杂性, 若是能结合高级语言的流程控制等特性, 将会有很大优势.
高级语言中使用嵌入式语言需要解决的问题程序与数据库连接
SQL语句在执行过程中,必须有提交和撤消语句才能确认其操作结果.
- SQL执行的提交:
exec sql commit work; - SQL执行的撤消:
exec sql rollback work;
为什么需要提交和撤销?
先了解一下事务的概念:
事务
(从应用程序员角度)是一个存取或改变数据库内容的程序的一次执行,或者说一条或多条SQL语句的一次执行被看作一个事务.
事务一般是由应用程序员提出,因此有开始和结束, 结束前需要提交或撤消。
Begin Transaction
exec sql …
…
exec sql …
exec sql commit work | exec sql rollback work
End Transaction
在嵌入式SQL程序中,任何一条数据库操纵语句(如exec sql select等)都会引发一个新事务的开
始,只要该程序当前没有正在处理的事务。而事务的结束是需要应用程序员通过commit或
rollback确认的。因此Begin Transaction和End Transaction两行语句是不需要的。
事务, 就是提供一组操作, 要么全部执行, 要么全部不执行, 保证这个这组操作过程的完整性.
事务的特性:
-
原子性Atomicity
DBMS能够保证事务的一组更新操作是原子不可分的,即对DB而言,要么全做,要么全不做 -
一致性Consistency
DBMS保证事务的操作状态是正确的,符合一致性的操作规则,它是进一步由隔离性来保证的 -
隔离性Isolation
DBMS保证并发执行的多个事务之间互相不受影响。例如两个事务T1和T2, 即使并发执行,也相当于或者先执行了T1,再执行T2; 或者先执行了T2, 再执行T1 -
持久性Durability: DBMS保证已提交事务的影响是持久的,被撤销事务的影响是可恢复的.
嵌入式SQL示例:
#include <stdio.h>
#include “prompt.h”
exec sql include sqlca; // SQLCA: SQL communication, Area SQL通信区
char cid_prompt[ ] = "Please enter customer id: ";
int main()
{
exec sql begin declare section; // 变量声明
char cust_id[5], cust_name[14];
float cust_discnt;
char user_name[20],user_pwd[20];
exec sql end declare section;
exec sql whenever sqlerror goto report_error; // SQL错误捕获语句
exec sql whenever not found goto notfound; // SQL错误捕获语句
strcpy(user_name, "poneilsql"); // 连接数据库
strcpy(user_pwd, "*****");
exec sql connect :user_name identified by :user_pwd;
while((prompt(cid_prompt, 1, cust_id, 4)) >=0) {
exec sql select cname,discnt
into :cust_name, :cust_discnt
from customers where cid=:cust_id;
exec sql commit work; // SQL Commit Wort
printf("Customer’s name is %s and discount is %5.1f\n", cust_name,cust_discnt);
continue;
notfound: printf("Can’t find customer %s, continuing\n", cust_id);
}
exec sql commit release;
return 0;
report_error:
print_dberror(); // 打印错误
exec sql rollback release; // 断开连接
return 1;
}
游标
游标是什么? 我们为什么需要它?
检索单行结果,可将结果直接传送到宿主程序的变量中.例如:
exec sql select Sname, Sage into :vSname, :vSage from Student where Sname = :specName ;
检索多行结果, 就需要使用游标(Cursor)
- 游标是指向某检索记录集的指针
- 通过这个指针的移动, 每次读一行处理一行, 再读一行...., 直到处理完毕
- 读一行通过Fetch...into语句实现, 每次Fetch, 都是向下移动指针, 然后再读取
- 记录集有结束标识EOF, 用来标记后面已没有记录了
游标的使用:
游标(Cursor)的使用需要先定义、再打开(执行)、接着一条接一条处理,最后再关闭. 可以定义一次, 多次打开.
exec sql declare cur_student cursor for
select Sno, Sname, Sclass from Student where Sclass='035101' ;
exec sql open cur_student;
exec sql fetch cur_student into :vSno, :vSname, :vSclass;
… …
exec sql close cur_student;
可滚动的游标
标准的游标总是从上往下的读的. 但是我们想向上向下滚动读取. ODBC(Open DataBase Connectivity)是一种跨DBMS的DB操作平台, 它在应用程序与实际的DBMS之间提供了一种通用接口, 可以帮助我们实现滚动游标.
数据库记录的删除与更新
删除
查找删除:
exec sql delete from customers c where c.city = 'Harbin' and
not exists ( select * from orders o where o.cid = c.cid);
定位删除:
exec sql declare delcust cursor for
select cid from customers c where c.city =‘harbin’ and
not exists ( select * from orders o where o.cid = c.cid)
for update of cid;
exec sql open delcust
While (TRUE) {
exec sql fetch delcust into :cust_id;
exec sql delete from customers where current of delcust ;
}
更新
一种是查找更新, 一种是定位更新
查找更新:
exec sql update student s set sclass = "035102"
where s.sclass = "034101"
定位更新:
exec sql declare stud cursor for
select * from student s where s.sclass ="034101"
for update of sclass;
exec sql open stud
While (TRUE) {
exec sql fetch stud into :vSno, :vSname, :vSclass;
exec sql update student set sclass = "035102" where current of stud ;
}
数据库插入
exec sql insert into student ( sno, sname, sclass)
values ('03510128', '张三', '035101') ;
状态捕获及其处理
状态, 是嵌入式SQL语句的执行状态, 尤其指一些出错状态, 有时程序需要知道这些状态并对这些状态进行处理.
嵌入式SQL程序中, 状态捕获及处理有三部分构成:
- 设置SQL通信区: 一般在嵌入式SQL程序的开始处便设置
exec sql include sqlca; - 设置状态捕获语句: 在嵌入式SQL程序的任何位置都可以设置, 可以多次设置, 但是有作用域
exec sql whenever sqlerror goto report_error; - 状态处理语句: 某一段程序以应对SQL的某种状态
report_error: exec sql rollback;
SQL通信区: SQLCA
- SQLCA是一个已被声明过的具C语言的结构形式的内存信息区,其中的成员变量用来记录SQL语句执行的状态,便于宿主程序读取与处理
-
SQLCA是DBMS(执行SQL语句)与宿主程序之间交流的桥梁之一
状态捕获语句
exec sql whenever condition action;
Whenever语句的作用是设置一个"条件陷阱", 当满足条件的时候, 就会执行相应的动作.
条件(condition)与动作(action)
作用范围, 两个whenever 之间, 否则直到程序结束.
注意:作用域是语句在程序中的位置,而不是控制流程(因是预编译程序处理条件陷阱)