十九、游标详解

代码中被[]包含的表示可选,|符号分开的表示可选其一。

需求背景

当我们需要对一个select的查询结果进行遍历处理的时候,如何实现呢?

此时我们需要使用游标,通过游标的方式来遍历select查询的结果集,然后对每行数据进行处理。

本篇内容

  • 游标定义
  • 游标作用
  • 游标使用步骤
  • 游标执行过程详解
  • 单游标示例
  • 嵌套游标示例

准备数据

  创建库:javacode2018

  创建表:test1、test2、test3

  /*建库javacode2018*/  drop database if exists javacode2018;  create database javacode2018;

  /*切换到javacode2018库*/  use javacode2018;

  DROP TABLE IF EXISTS test1;  CREATE TABLE test1(a int,b int);  INSERT INTO test1 VALUES (1,2),(3,4),(5,6);

  DROP TABLE IF EXISTS test2;  CREATE TABLE test2(a int);  INSERT INTO test2 VALUES (100),(200),(300);

  DROP TABLE IF EXISTS test3;  CREATE TABLE test3(b int);  INSERT INTO test3 VALUES (400),(500),(600);

一、游标定义

  游标(Cursor)是处理数据的一种方法,为了查看或者处理结果集中的数据,游标提供了在结果集中一次一行遍历数据的能力。

  游标只能在存储过程和函数中使用。

二、游标的作用

  如sql:

  select a,b from test1;

  上面这个查询返回了test1中的数据,如果我们想对这些数据进行遍历处理,此时我们就可以使用游标来进行操作。

  游标相当于一个指针,这个指针指向select的第一行数据,可以通过移动指针来遍历后面的数据。

三、游标的使用步骤

  声明游标:这个过程只是创建了一个游标,需要指定这个游标需要遍历的select查询,声明游标时并不会去执行这个sql。

  打开游标:打开游标的时候,会执行游标对应的select语句。

  遍历数据:使用游标循环遍历select结果中每一行数据,然后进行处理。

  关闭游标:游标使用完之后一定要关闭。

四、游标语法

  声明游标

  DECLARE 游标名称 CURSOR FOR 查询语句;

一个begin end中只能声明一个游标。

  打开游标

open 游标名称;

  遍历游标

  fetch 游标名称 into 变量列表;

取出当前行的结果,将结果放在对应的变量中,并将游标指针指向下一行的数据。

当调用fetch的时候,会获取当前行的数据,如果当前行无数据,会引发mysql内部的NOT FOUND错误。

  关闭游标

  close 游标名称;

游标使用完毕之后一定要关闭。

五、单游标示例

    写一个函数,计算test1表中a、b字段所有的和。

  创建函数:

    /*删除函数*/
    DROP FUNCTION IF EXISTS fun1;
    /*声明结束符为$*/
    DELIMITER $
    /*创建函数*/
    CREATE FUNCTION fun1(v_max_a int)
      RETURNS int
      BEGIN
        /*用于保存结果*/
        DECLARE v_total int DEFAULT 0;
        /*创建一个变量,用来保存当前行中a的值*/
        DECLARE v_a int DEFAULT 0;
        /*创建一个变量,用来保存当前行中b的值*/
        DECLARE v_b int DEFAULT 0;
        /*创建游标结束标志变量*/
        DECLARE v_done int DEFAULT FALSE;
        /*创建游标*/
        DECLARE cur_test1 CURSOR FOR SELECT a,b from test1 where a<=v_max_a;
        /*设置游标结束时v_done的值为true,可以v_done来判断游标是否结束了*/
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done=TRUE;
        /*设置v_total初始值*/
        SET v_total = 0;
        /*打开游标*/
        OPEN cur_test1;
        /*使用Loop循环遍历游标*/
        a:LOOP
          /*先获取当前行的数据,然后将当前行的数据放入v_a,v_b中,如果当前行无数据,v_done会被置为true*/
          FETCH cur_test1 INTO v_a, v_b;
          /*通过v_done来判断游标是否结束了,退出循环*/
          if v_done THEN
            LEAVE a;
          END IF;
          /*对v_total值累加处理*/
          SET v_total = v_total + v_a + v_b;
        END LOOP;
        /*关闭游标*/
        CLOSE cur_test1;
        /*返回结果*/
        RETURN v_total;
      END $
    /*结束符置为;*/
    DELIMITER ;    

    上面语句执行过程中可能有问题,解决方式如下。

    错误信息:Mysql 创建函数出现This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA

    This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary

    mysql的设置默认是不允许创建函数

    解决办法1:

    执行:

    SET GLOBAL log_bin_trust_function_creators = 1;

    不过 重启了 就失效了

    注意:有主从复制的时候 从机必须要设置  不然会导致主从同步失败

    解决办法2:

    在my.cnf里面设置

    log-bin-trust-function-creators=1

    不过这个需要重启服务

六、游标过程详解  

  以上面的示例代码为例,咱们来看一下游标的详细执行过程。

  游标中有个指针,当打开游标的时候,才会执行游标对应的select语句,这个指针会指向select结果中第一行记录

  当调用fetch 游标名称时,会获取当前行的数据,如果当前行无数据,会触发NOT FOUND异常。

  当触发NOT FOUND异常的时候,我们可以使用一个变量来标记一下,如下代码:

  DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done=TRUE;

  当游标无数据触发NOT FOUND异常的时候,将变量v_down的值置为TURE,循环中就可以通过v_down的值控制循环的退出。

  如果当前行有数据,则将当前行数据存到对应的变量中,并将游标指针指向下一行数据,如下语句:

   fetch 游标名称 into 变量列表;

七、嵌套游标

写个存储过程,遍历test2、test3,将test2中的a字段和test3中的b字段任意组合,插入到test1表中。

  创建存储过程:

  /*删除存储过程*/  DROP PROCEDURE IF EXISTS proc1;  /*声明结束符为$*/  DELIMITER $  /*创建存储过程*/  CREATE PROCEDURE proc1()    BEGIN      /*创建一个变量,用来保存当前行中a的值*/      DECLARE v_a int DEFAULT 0;      /*创建游标结束标志变量*/      DECLARE v_done1 int DEFAULT FALSE;      /*创建游标*/      DECLARE cur_test1 CURSOR FOR SELECT a FROM test2;      /*设置游标结束时v_done1的值为true,可以v_done1来判断游标cur_test1是否结束了*/      DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done1=TRUE;      /*打开游标*/      OPEN cur_test1;      /*使用Loop循环遍历游标*/      a:LOOP        FETCH cur_test1 INTO v_a;        /*通过v_done1来判断游标是否结束了,退出循环*/        if v_done1 THEN          LEAVE a;        END IF;

        BEGIN          /*创建一个变量,用来保存当前行中b的值*/          DECLARE v_b int DEFAULT 0;          /*创建游标结束标志变量*/          DECLARE v_done2 int DEFAULT FALSE;          /*创建游标*/          DECLARE cur_test2 CURSOR FOR SELECT b FROM test3;          /*设置游标结束时v_done1的值为true,可以v_done1来判断游标cur_test2是否结束了*/          DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done2=TRUE;

          /*打开游标*/          OPEN cur_test2;          /*使用Loop循环遍历游标*/          b:LOOP            FETCH cur_test2 INTO v_b;            /*通过v_done1来判断游标是否结束了,退出循环*/            if v_done2 THEN              LEAVE b;            END IF;

            /*将v_a、v_b插入test1表中*/            INSERT INTO test1 VALUES (v_a,v_b);          END LOOP b;          /*关闭cur_test2游标*/          CLOSE cur_test2;        END;

      END LOOP;      /*关闭游标cur_test1*/      CLOSE cur_test1;    END $  /*结束符置为;*/  DELIMITER ;

十、总结

  1. 游标用来对查询结果进行遍历处理
  2. 游标的使用过程:声明游标、打开游标、遍历游标、关闭游标
  3. 游标只能在存储过程和函数中使用
  4. 一个begin end中只能声明一个游标
  5. 掌握单个游标及嵌套游标的使用
 

原文地址:https://www.cnblogs.com/biao/p/11775704.html

时间: 2024-10-09 21:48:45

十九、游标详解的相关文章

java提高篇(九)-----详解匿名内部类

摘自http://blog.csdn.net/chenssy/article/details/13170015 java提高篇(九)-----详解匿名内部类 在Java提高篇-----详解内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节问题,所以就衍生出这篇博客.在这篇博客中你可以了解到匿名内部类的使用.匿名内部类要注意的事项.如何初始化匿名内部类.匿名内部类使用的形参为何要为final. 一.使用匿名内部类内部类 匿名内部类由于没有名字,所以它的创建方式有点儿奇怪.创建格式

(十)Maven依赖详解

1.何为依赖? 比如你是个男的,你要生孩子,呸呸呸...男的怎么生孩子,所以你得依赖你老婆,不过也不一定咯,你也可以依赖其她妹子. 我们在平时的项目开发中也是同理,你需要依赖一些东西才能实现相应的功能,但相应的功能或许也可以依赖其它的东西实现,比如数据库操作吧,你可以依赖hibernate,但你也可以通过mybatis来做. 这就是所谓的依赖关系咯. 以前我们需要手动的去找hibernate或者mybatis的jar包,系统抛异常我们还不知哪里报错,通过琢磨才明白没有引入相应的jar包,然后就去

面向对象编程(十六)——内部类详解

一.内部类(innerclasses) 一般情况,我们把类定义成独立的单元.有些情况下,我们把一个类放在另一个类的内部定义,称为内部类. 在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类.广泛意义上的内部类一般来说包括这四种:成员内部类.局部内部类.匿名内部类和静态内部类. 1. 内部类的作用 内部类提供了更好的封装,只能让外部类直接访问,不允许同一个包中的其他类直接访问. 内部类可以直接访问外部类的私用属性.内部类被当成其外部类的成员.但是外部类不能访问内部类的

十四、详解事务

本篇内容 什么是事务,它有什么用? 事务的几个特性 事务常见操作指令详解 事务的隔离级别详解 脏读.不可重复读.可重复读.幻读详解 演示各种隔离级别产生的现象 关于隔离级别的选择 一.什么是事务? 数据库中的事务是指对数据库执行一批操作,这些操作最终要么全部执行成功,要么全部失败,不会存在部分成功的情况. 二.事务的几个特性(ACID) 原子性(Atomicity) 事务的整个过程如原子操作一样,最终要么全部成功,或者全部失败,这个原子性是从最终结果来看的,从最终结果来看这个过程是不可分割的.

SQLServer游标详解

一.游标概念 我们知道,关系数据库所有的关系运算其实是集合与集合的运算,它的输入是集合输出同样是集合,有时需要对结果集逐行进行处理,这时就需要用到游标.我们对游标的使用一本遵循"五步法":声明游标->打开游标->读取数据->关闭游标->删除游标.以下就从这五步对游标的使用进行说明,并给出具体实例. 二."五步法"讲解 1.声明游标(DECLARE CURSOR) (1) DECLARE CURSOR 既接受基于 ISO 标准的语法,也接受使用

PL/SQL 游标详解

刚打开游标的时候,是位于一个空行,要用fetch into 才能到第一行.只是要注意用更新游标的时候,不能在游标期间commit. 否则会报ORA-01002: fetch out of sequence就是COMMIT导致的错误.在打开有for update的cursor时,系统会给取出的数据加上排他锁(exclusive), 这样在这个锁释放前其他用户不能对这些记录作update.delete和加锁.而我一旦执行了commit,锁就释放了,游标也变成无效的,再去fetch数据时就出现错误了.

SQLServer 游标详解

一.用到的数据 CREATE TABLE [dbo].[XSB]( [学号] [char](6) NOT NULL, [姓名] [char](8) NOT NULL, [性别] [bit] NULL, [出生时间] [date] NULL, [专业] [char](12) NULL, [总学分] [int] NULL, [备注] [varchar](500) NULL, PRIMARY KEY CLUSTERED ( [学号] ASC )WITH (PAD_INDEX = OFF, STATIS

Redis教程(十):持久化详解

转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/137.html 一.Redis提供了哪些持久化机制: 1). RDB持久化:    该机制是指在指定的时间间隔内将内存中的数据集快照写入磁盘.        2). AOF持久化:    该机制将以日志的形式记录服务器所处理的每一个写操作,在Redis服务器启动之初会读取该文件来重新构建数据库,以保证启动后数据库中的数据是完整的.    3). 无持久化:    我们可以

JAVA学习笔记(四十六)- 内部类详解

成员内部类 /* * 内部类 * 定义在另一个类中的类,称为内部类Inner Class * 包含内部类的类,称为外部类Outer Class * * 应用场合:在窗体程序中进行事件处理 * * 分类: * 成员内部类 * 局部内部类 * 静态内部类 * 匿名内部类 * * 成员内部类 * 1.在外部类中访问内部类,可以访问内部类中的所有成员,包含private修饰的 * 2.在外部类外访问内部类,不能访问内部类中的private修饰的成员 * 3.在内部类中访问外部类,直接访问,如果内部类和外