式是系统的缺省模式。嵌入SQL的事务语法和T-SQL的事务语法是相同的。 2.3.8.1 T-SQL事务模式 1)、开始事务
exec sql [at connect_name]
begin transaction [ transaction_name]; 2)、保存事务回滚点 exec sql [at connect_name] save transaction [ savepoint_name]; 3)、提交事务
exec sql [at connect_name] commit transaction [ transaction_name]; 4)、回滚事务
exec sql [at connect_name] rollback transaction [ savepoint_name | transaction_name]; 2.3.8.2 ANSI/ISO事务模式
该模式没有begin transaction和save transaction。在应用程序中,只要遇到以下语句,就表示事务开始:delete、insert、select、update、open和exec。当遇到commit work或rollback work,就表示事务结束。也就是说,commit和rollback表示当前事务结束,下一个事务开始。 2.4动态SQL语句
前一节中讲述的嵌入SQL语言都是静态SQL语言,即在编译时已经确定了引用的表和列。主变量不改变表和列信息。在上几节中,我们使用主变量改变查询参数,但是不能用主变量代替表名或列名。否则,系统报错。动态SQL语句就是来解决这个问题。
动态SQL语句的目的是,不是在编译时确定SQL的表和列,而是让程序在运行时提供,并将SQL语句文本传给DBMS执行。静态SQL语句在编译时已经生成执行计划。而动态SQL语句,只有在执行时才产生执行计划。动态SQL语句首先执行PREPARE语句要求DBMS分析、确认和优化语句,并为其生成执行计划。DBMS还设置SQLCODE以表明语句中发现的错误。当程序执行完―PREPARE‖语句后,就可以用EXECUTE语句执行执行计划,并设置SQLCODE,以表明完成状态。 使用动态SQL,共分成四种方法: 方法 支持的SQL语句 实现方法
1 该语句内不包含宿主变量,该语句不是查询语句 execute immediate 2 该语句内包含输入宿主变量 ,该语句不是查询语句 prepare和execute 3 包含已知数目的输入宿主变量或列的查询 prepare和fetch
4 包含未知数目的输入宿主变量或列的查询 prepare和fetch,用描述符
按照功能和处理上的划分,动态SQL应该分成两类来解释:动态修改和动态查询。方法1和方法2完成动态修改(参见2.4.1)。方法3和方法4完成了动态查询(参见2.4.2和2.4.3)。 2.4 .1 动态修改
方法1和方法2完成动态修改。对于方法1,表示要执行一个完整的T-SQL语句,该语句没有宿主变量,不是一个查询语句。因为没有宿主变量来带入不同的参数,所以不能通过方法1来重复执行修改语句。具体语法为:
exec sql [at connection_name] execute immediate {: host_variable | string};
其中,host_variable和string是存放完整T-SQL语句。
例:提示用户输入被更新书的条件,然后组合成为一个完整的SQL语句,并执行更新。 exec sql begin declare section; CS_CHAR sqlstring[200]; exec sql end declare section; char cond[150];
exec sql whenever sqlerror call err_p(); exec sql whenever sqlwarning call warn_p(); strcpy(sqlstring,
update titles set price=price*1.10 where ); printf(Enter search condition:); scanf(%s, cond); strcat(sqlstring, cond);
exec sql execute immediate :sqlstring; exec sql commit work;
对于方法2,可以执行一个包含输入宿主变量的动态修改语句。该方法要使用PREPARE语句和EXECUTE语句。PREPARE语句是动态SQL语句独有的语句。其语法为: PREPARE 语句名 FROM 宿主变量|字符串
该语句接收含有SQL语句串的宿主变量,并把该语句送到DBMS。DBMS编译语句并生成执行计划。在语句串中包含一个―?‖表明参数,当执行语句时,DBMS需要参数来替代这些―?‖。PREPRARE执行的结果是,DBMS用语句名标志准备后的语句。SQL SERVER编译后的语句以临时存储过程的形式存放在缓冲区中。语句名类似于游标名,是一个SQL标识符。在执行SQL语句时,EXECUTE语句后面是这个语句名。请看下面这个例子:
EXEC SQL BEGIN DECLARE SECTION;
char prep[] = INSERT INTO mf_table VALUES(?,?,?); char name[30]; char car[30]; double num;
EXEC SQL END DECLARE SECTION; EXEC SQL PREPARE prep_stat FROM :prep; while (SQLCODE == 0) {
strcpy(name, Elaine); strcpy(car, Lamborghini); num = 4.9;
EXEC SQL EXECUTE prep_stat USING :name, :car, :num; }
在这个例子中,prep_stat是语句名,prep宿主变量的值是一个INSERT语句,包含了三个参数(3个―?‖)。PREPARE的作用是,DBMS编译这个语句并生成执行计划,并把语句名标志这个准备后的语句。值得注意的是,PREPARE中的语句名的作用范围为整个程序,所以不允许在同一个程序中使用相同的语句名在多个PREPARE语句中。
EXECUTE语句是动态SQL独有的语句。它的语法如下: EXECUTE 语句名 USING 宿主变量 | DESCRIPTOR 描述符名
请看上面这个例子中的―EXEC SQL EXECUTE prep_stat USING :name, :car, :num;‖语句,它的作用是,请求DBMS执行PREPARE语句准备好的语句。当要执行的动态语句中包含一个或多个参数标志时,在
EXECUTE语句必须为每一个参数提供值,如::name、:car和:num。这样的话,EXECUTE语句用宿主变量值逐一代替准备语句中的参数标志(―?‖),从而,为动态执行语句提供了输入值。
使用主变量提供值,USING子句中的主变量数必须同动态语句中的参数标志数一致,而且每一个主变量的数据类型必须同相应参数所需的数据类型相一致。各主变量也可以有一个伴随主变量的指示符变量。当处理EXECUTE语句时,如果指示符变量包含一个负值,就把NULL值赋予相应的参数标志。除了使用主变量为参数提供值,也可以通过SQLDA提供值(见节2.4.4)。 2.4.2 动态游标
使用动态游标可以完成方法3。
游标分为静态游标和动态游标两类。对于静态游标,在定义游标时就已经确定了完整的SELECT语句。在SELECT语句中可以包含主变量来接收输入值。当执行游标的OPEN语句时,主变量的值被放入SELECT语句。在OPEN语句中,不用指定主变量,因为在DECLARE CURSOR语句中已经放置了主变量。请看下面静态游标的例子:
EXEC SQL BEGIN DECLARE SECTION; char szLastName[] = White; char szFirstName[30];
EXEC SQL END DECLARE SECTION; EXEC SQL
DECLARE author_cursor CURSOR FOR
SELECT au_fname FROM authors WHERE au_lname = :szLastName; EXEC SQL OPEN author_cursor;
EXEC SQL FETCH author_cursor INTO :szFirstName;
动态游标和静态游标不同。以下是动态游标使用的句法(请参照本小节后面的例子来理解动态游标)。 1)、声明游标:
对于动态游标,在DECLARE CURSOR语句中不包含SELECT语句。而是,定义了在PREPARE中的语句名,PREPARE语句规定与查询相关的语句名称。具体语法为: exec sql [at connection_name] declare cursor_name cursor for statement_name;
如:EXEC SQL DECLARE author_cursor CURSOR FOR select_statement; 值得注意的是,声明动态游标是一个可执行语句,应该在PREPARE语句后执行。 2)、打开游标
完整语法为:OPEN 游标名 [USING 主变量名 | DESCRIPTOR 描述名]
在动态游标中,OPEN语句的作用是使DBMS定位相关的游标在第一行查询结果前。当OPEN语句成功执行完毕后,游标处于打开状态,并为FETCH语句做准备。OPEN语句执行一条由PREPARE语句预编译的语句。如果动态查询正文中包含有一个或多个参数标志时,OPEN语句必须为这些参数提供参数值。USING子句的作用就是规定参数值。可以使用主变量提供参数值,也可以通过描述名(即SQLDA)提供参数值。如:EXEC SQL OPEN author_cursor USING :szLastName;。 3)、取一行值
FETCH语法为:FETCH 游标名 INTO USING DESCRIPTOR 描述符名。
动态FETCH语句的作用是,把游标移到下一行,并把这一行的各列值送到SQLDA中。注意的是,静态FETCH语句的作用是用主变量表接收查询到的列值。在方法3中,使用的是静态FETCH语句获得值。动态FETCH语句只在方法4中使用。 4)、关闭游标
如:EXEC SQL CLOSE c1;
关闭游标的同时,会释放由游标添加的锁和放弃未处理的数据。在关闭游标前,该游标必须已经声明和打开。另外,程序终止时,系统会自动关闭所有打开的游标。
总之,在动态游标的DECLARE CURSOR语句中不包含SELECT语句。而是,定义了在PREPARE中的语句名,用PREPARE语句规定与查询相关的语句名称。当PREPARE语句中的语句包含了参数,那么在OPEN语句中必须指定提供参数值的主变量或SQLDA。动态DECLARE CURSOR语句是一个可执行语句。该子句必须在OPEN、FETCH、CLOSE语句之前使用。请看下面这个例子,描述了完成方法3的五个步骤:PREPARE、DECLARE、OPEN、FETCH和CLOSE。 ……
EXEC SQL BEGIN DECLARE SECTION;
char szCommand[] = SELECT au_fname FROM authors WHERE au_lname = ?; char szLastName[] = White; char szFirstName[30];
EXEC SQL END DECLARE SECTION;
EXEC SQL PREPARE select_statement FROM :szCommand;
EXEC SQL DECLARE author_cursor CURSOR FOR select_statement; EXEC SQL OPEN author_cursor USING :szLastName; EXEC SQL FETCH author_cursor INTO :szFirstName; EXEC SQL CLOSE author_cursor; ………
下面是一个实现方法3的实际例子。提示用户输入排序的条件,并把符合条件的书信息显示出来。 ……
exec sql begin declare section; CS_CHAR sqlstring[200]; CS_FLOAT bookprice,condprice; CS_CHAR booktitle[200]; exec sql end declare section; char orderby[150];
exec sql whenever sqlerror call err_p(); exec sql whenever sqlwarning call warn_p(); strcpy(sqlstring,
select title,price from titles\\ where price>? order by ); printf(Enter the order by clause:); scanf(%s, orderby); strcat(sqlstring, orderby);
exec sql prepare select_state from :sqlstring; exec sql declare select_cur cursor for select_state;
condprice = 10; /* 可以提示用户输入这个值*/ exec sql open select_cur using :condprice; exec sql whenever not found goto end; for (;;) {
exec sql fetch select_cur into :booktitle,:bookprice; printf( s %bookprice=%6.2f\\n, booktitle, bookprice); } end:
exec sql close select_cur; exec sql commit work; ……….. 2.4.3 SQLDA
要实现方法4,则需要使用SQLDA(也可以使用SQL Descriptors,请读者参阅帮助信息)。可以通过SQLDA为嵌入SQL语句提供不确定的输入数据和从嵌入SQ语句中输出不确定数据。理解SQLDA的结构是理解动态SQL的关键。
我们知道,动态SQL语句在编译时可能不知道有多少列信息。在嵌入SQL语句中,这些不确定的数据是通过SQLDA完成的。SQLDA的结构非常灵活,在该结构的固定部分,指明了多少列等信息(如下图中的sqld=2,表示为两列信息),在该结构的后面,有一个可变长的结构(sd_column结构),说明每列的信息。
SQLDA结构 Sd_Sqld=2 Sd_column ……
Sd_datafmt Sd_Sqllen Sd_sqldata …..
Sd_datafmt Sd_Sqllen Sd_Sqldata …..
图6-2 SQLDA结构示例
具体SQLDA的结构在sqlda.h中定义,是: typedef struct _sqlda {
CS_SMALLINT sd_sqln; CS_SMALLINT sd_sqld; struct _sd_column {
CS_DATAFMT sd_datafmt;