Prayer

在一般中寻求卓越
posts - 1256, comments - 190, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

用游标的SQL语句应用

Posted on 2008-09-10 09:56 Prayer 阅读(900) 评论(0)  编辑 收藏 引用 所属分类: 数据库,SQLC/C++


 必须使用游标的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语句。

 

 


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理