这种方法与方法2相似,只是将PREPARE语句与游标结合使用,这样动态SQL就可以处理返回多行的查询语句了。事实上,如果需要处理的动态SQL语句是返回多行元组的查询语句,也只能使用方法3或者方法4。
这种方法允许应用程序接受或者构建一个动态查询,然后用PREPARE命令和DECLARE、OPEN、FETCH以及CLOSE这五个游标命令一起处理该查询。
查询列的数目、输入主变量的占位符数目和输入主变量的类型必须在编译时给出,而数据库对象名,比如表名、列名等,可以在运行的时候确定。SELECT语句的子句也可以到运行时再给出。方法3的格式如下:
PREPARE FROM <命令标识符> FROM <参数标识符>;
DECLARE <游标名> CURSOR FOR <命令标识符>;
OPEN <游标名> [USING <参数标识符>];
FETCH <游标名> INTO <参数标识符>;
CLOSE <游标名>;
其中,PREPARE准备好一个<命令标识符>来代表需要执行的查询语句,然后对该<命令标识符>定义一个游标,这个动作不仅为游标命名,而且把这个<游标名>关联到相应的<命令标识符>。下一步,就可以像静态SQL一样打开游标,不同之处在于打开时用户可以使用USING子句输入准备命令时留出的输入主变量占位符所需要的输入主变量,如果输入的变量个数和占位符的个数不符,那么系统会给出参数个数不匹配的错误消息,并设置相应的错误码。如果输入主变量的个数和类型都正确,那么游标就能正确打开。在打开的过程中,系统就执行了准备好的SQL语句,并且得到结果的活动集。游标打开以后,就可以和静态SQL语句一样对游标进行FETCH操作,得到活动集中的结果值。如果INTO子句中输出主变量的个数和类型与活动集中元组的列不匹配,系统会给出相应的错误信息,并且设置错误码。
下面给出一个使用PREPARE和游标的例子,源程序如下:
例 8-2
/********************************************************
* esql*C 使用PREPARE和游标的实例*
********************************************************/
#include <stdio.h>
#include <stdlib.h>
void error()
{
/* 对错误情况不予处理,以防止死循环*/
EXEC SQL WHENEVER SQLERROR CONTINUE;
/* 输出错误信息*/
sqlprint();
EXEC SQL ROLLBACK WORK;
exit(1);
}
int main()
{
/*声明宿主数组变量*/
EXEC SQL BEGIN DECLARE SECTION;
char command[80];
char hnum[4];
char hname[21];
EXEC SQL END DECLARE SECTION;
/* 对错误情况进行处理 */
EXEC SQL WHENEVER SQLERROR do error();
EXEC SQL WHENEVER NOT FOUND goto not_found;
/*获取动态命令*/
sprintf(command,”SELECT empname FROM employee
WHERE empnum = ?”);
printf(“请输入要获得姓名的员工编号:\\n”);
scanf(“%s”, hnum);
/* 连接神通数据库 */
EXEC SQL CONNECT TO sql92test@localhost USER
oscar USING zjuhxy;
/* prepare */
EXEC SQL PREPARE prep_emp FROM :command;
/*声明一个跟该动态语句相关的游标*/
EXEC SQL DECLARE cur_emp CURSOR FOR prep_emp;
/*通过OPEN操作向动态语句传递参数*/
EXEC SQL OPEN cur_emp USING :hnum ;
EXEC SQL FETCH cur_emp INTO :hname;
/* 此处可以输出找到的empname */
printf(“员工%s的名字是:%s.\\n”, hnum, hname);
/* 用户没有调用关闭游标语句,但是系统会在事务结束时自动关闭游标 */
EXEC SQL COMMIT WORK;
EXEC SQL DISCONNECT;
exit(0);
not_found:
printf(“员工 %s 不存在.\\n”, hnum);
EXEC SQL ROLLBACK WORK;
EXEC SQL DISCONNECT;
exit(0);
}