必须使用游标的SQL语句有:
·查询结果为多条记录的SELECT语句
·CURRENT形式的UPDATE语句
·CURRENT形式的DELETE语句
一、查询结果为多条记录的SELECT语句
对于SELECT语句查询结果为多条记录时,必须以游标机制作为桥梁,将多条记录一次一条送至宿主程序处理,从而把对集合的操作转换为对单个记录的处理。
前面已经讨论了使用游标的步骤为:
说明游标:EXEC SQL DECLARE <游标名> CURSOR FOR <SELECT语句>;
打开游标:EXEC SQL OPEN <游标名>;
推进游标指针并取当前记录:
EXEC SQL FETCH <游标名>
INTO <主变量>[<指示变量>][,<主变量>[<指示变量>]]...;
关闭游标:EXEC SQL CLOSE <游标名>;
以下以实例讨论多记录的SELECT嵌入式语句的应用:
例1 查询某个系全体学生的信息。要查询的系名由用户在程序运行过程中指定,放在主变量deptname中
......
......
EXEC SQL BEGIN DECLARE SECTION;
......
/* 说明主变量 deptname,HSno,HSname,HSsex,HSage等*/
......
......
EXEC SQL END DECLARE SECTION;
......
......
gets(deptname); /* 为主变量deptname赋值 */
......
EXEC SQL DECLARE SX CURSOR FOR
SELECT Sno, Sname, Ssex, Sage
FROM Student
WHERE SDept=:deptname; /* 说明游标 */
EXEC SQL OPEN SX /* 打开游标 */
WHILE(1) /* 用循环结构逐条处理结果集中的记录 */
{
EXEC SQL FETCH SX INTO :HSno, :HSname, :HSsex, :HSage;
/* 游标指针向前推进一行,然后从结果集中取当前行,送相应主变量 */
if (sqlca.sqlcode <> SUCCESS)
break;
/* 若所有查询结果均已处理完或出现SQL语句错误,则退出循环 */
/* 由主语言语句进行进一步处理 */
......
......
};
EXEC SQL CLOSE SX; /* 关闭游标 */
......
......
说明:
*) 本例要查询deptname系的所有学生的学号、姓名、性别和年龄。
*) 首先定义游标SX,将其与查询结果集(即deptname系的所有学生的学号、姓名、性别和年龄)相联系(语句①)。这时相应的SELECT语句并没有真正执行。
*) 然后打开游标SX,这时DBMS执行与SX与相联系的SELECT语句,即查询deptname系的所有学生的学号、姓名、性别和年龄(语句②),之后SX处于活动状态。
*) 接下来在一个循环结构中逐行取结果集中的数据,分别将学号Sno、姓名Sname、性别Ssex和年龄Sage送至主变量HSno、HSname、HSsex和HSage中(语句③)。主语言语句将对这些主变量做进一步处理。
*) 最后关闭游标SX(语句④)。这时SX不再与deptname系的学生数据相联系。
*) 被关闭的游标SX实际上可以再次被打开,与新的查询结果相联系。例如,可以在例1中再加上一层外循环,每次对deptname赋新的值,这样SX就每次和不同的系的学生集合相联系。如例2所示。
例2 查询某些系全体学生的选课信息。
......
......
EXEC SQL BEGIN DECLARE SECTION;
......
/* 说明主变量 deptname,HSno,HSname,HSsex,HSage等*/
......
......
EXEC SQL END DECLARE SECTION;
......
......
......
EXEC SQL DECLARE SX CURSOR FOR
SELECT Sno, Sname, Ssex, Sage
FROM Student
WHERE SDept=:deptname; /* 说明游标 */
WHILE (gets(deptname)!=NULL) /* 接收主变量deptname的值 */
{
/* 以下处理deptname指定系学生信息,每次循环中deptname可具不同值*/
EXEC SQL OPEN SX /* 打开游标 */
WHILE (1)
{ /* 用循环结构逐条处理结果集中的记录 */
EXEC SQL FETCH SX INTO :HSno, :HSname, :HSsex, :HSage;
/* 游标指针向前推进一行,然后取当前行送相应主变量 */
if (sqlca.sqlcode <> SUCCESS)
break;
/* 若所有结果处理完毕或出现语句错误,则退出循环 */
/* 由主语言语句进行进一步处理 */
......
......
}; /* 内循环结束 */
EXEC SQL CLOSE SX; /* 关闭游标 */
}; /* 外循环结束 */
非CURRENT形式的UPDATE语句和DELETE语句都是集合操作,一次修改或删除所有满足条件的记录。而如果只想修改或删除其中某个记录,则需要用带游标的SELECT语句查出所有满足条件的记录,从中进一步找出要修改或删除的记录,然后修改或删除之。具体步骤是:
(1) 说明游标
如果是为CURRENT形式的UPDATE语句作准备,则SELECT语句中要用 FOR UPDATE OF <列名>
子句指明将来检索出的数据在指定列是可修改的。
如果是为CURRENT形式的DELETE语句作准备,则不必使用上述子句。
(2) 打开游标
把所有满足查询条件的记录从指定表取到缓冲区中。
(3) 推进游标指针
并把当前记录从缓冲区中取出来送至主变量。
(4)检查该记录是否是要修改或删除的记录
如果是,则用UPDATE语句或DELETE语句修改或删除该记录。这时UPDATE语句和DELETE语句中要用
WHERE CURRENT OF <游标名>
表示修改或删除的是该游标中最近一次取出的记录,即游标指针指向的记录。
第3和4步通常用在一个循环结构中,通过循环执行FETCH语句,逐条取出结果集中的行进行判断和处理。
(5) 处理完毕关闭游标
释放结果集占用的缓冲区和其他资源。
例3 查询某个系全体学生的信息(要查询的系名由主变量deptname指定),然后根据用户的要求修改其中某些记录的年龄字段。
......
......
EXEC SQL BEGIN DECLARE SECTION;
......
/* 说明主变量 deptname,HSno,HSname,HSsex,HSage,NEWAge等*/
......
......
EXEC SQL END DECLARE SECTION;
......
......
gets(deptname); /* 为主变量deptname赋值 */
......
EXEC SQL DECLARE SX CURSOR FOR
SELECT Sno, Sname, Ssex, Sage
FROM Student
WHERE SDept=:deptname
FOR UPDATE OF Sage; /* 说明游标 */
EXEC SQL OPEN SX /* 打开游标 */
WHILE(1)
{ /* 用循环结构逐条处理结果集中的记录 */
EXEC SQL FETCH SX INTO :HSno, :HSname, :HSsex, :HSage;
/* 游标指针向前推进一行,然后从结果集中取当前行,送相应主变量 */
if (sqlca.sqlcode <> SUCCESS)
break;
/* 若所有查询结果均已处理完或出现SQL语句错误,则退出循环 */
printf("%s, %s, %s, %d", Sno, Sname, Ssex, Sage); /* 显示该记录 */
printf("UPDATE AGE ? "); /* 问用户是否要修改 */
scanf("%c",&yn);
if (yn='y' or yn='Y') /* 需要修改 */
{
printf("INPUT NEW AGE: ");
scanf("%d",&NEWAge); /* 输入新的年龄值 */
EXEC SQL UPDATE Student
SET Sage=:NEWAge
WHERE CURRENT OF SX; /* 修改当前记录的年龄字段 */
};
......
......
};
EXEC SQL CLOSE SX; /* 关闭游标 */
......
......
例4 查询某个系全体学生的信息(要查询的系名由主变量deptname指定),然后根据用户的要求修改删除其中某些记录。
......
......
EXEC SQL BEGIN DECLARE SECTION;
......
/* 说明主变量 deptname,HSno,HSname,HSsex,HSage等*/
......
......
EXEC SQL END DECLARE SECTION;
......
......
gets(deptname); /* 为主变量deptname赋值 */
......
EXEC SQL DECLARE SX CURSOR FOR
SELECT Sno, Sname, Ssex, Sage
FROM Student
WHERE SDept=:deptname; /* 说明游标 */
EXEC SQL OPEN SX /* 打开游标 */
WHILE(1)
{ /* 用循环结构逐条处理结果集中的记录 */
EXEC SQL FETCH SX INTO :HSno, :HSname, :HSsex, :HSage;
/* 游标指针向前推进一行,然后从结果集中取当前行,送相应主变量 */
if (sqlca.sqlcode <> SUCCESS)
break;
/* 若所有查询结果均已处理完或出现SQL语句错误,则退出循环 */
printf("%s, %s, %s, %d", Sno, Sname, Ssex, Sage); /* 显示该记录 */
printf("DELETE ? "); /* 问用户是否要删除 */
scanf("%c",&yn);
if (yn='y' or yn='Y') /* 需要删除 */
EXEC SQL DELETE
FROM Student
WHERE CURRENT OF SX; /* 删除当前记录 */
......
......
};
EXEC SQL CLOSE SX; /* 关闭游标 */
......
......
注意:
当游标定义中的SELECT语句带有UNION或ORDER BY子句时,或者该SELECT语句相当于定义了一个不可更新的视图时,不能使用CURRENT形式的UPDATE语句和DELETE语句。