
# 游标（Cursor）
#### 简介
游标（Cursor）用于处理SQL影响的结果集，允许逐行地访问和操作这些结果集。它是指向查询结果集的一个指针，可以对每一行数据进行单独的处理。游标主要分为显示游标和隐式游标。
#### 游标的属性
%FOUND：用于测试对游标进行FETCH的操作是否检索到行，如果FETCH检索到行，则返回TRUE。
%NOTFOUND：与%FOUND相反，如果FETCH操作没有检索到行，则返回TRUE。
%ISOPEN：用于判断游标是否打开。
%ROWCOUND：用于返回游标影响的行数。
#### 显式游标
显式游标是用户显式定义和控制的游标，用于处理返回多行数据的SELECT查询。用户需要显示地在PL/SQL中有显式的定义游标、打开游标、使用游标和关闭游标等步骤。
- 特点：
  - 灵活性：用户可以对查询结果进行精细的控制。可以逐行提取数据，并根据业务逻辑进行相应的处理。
  
  - 可扩展性：由于显式游标是显式定义的，因此可以轻松地修改或增加新的处理逻辑。
  
  - 复杂性：增加了代码的复杂性，用户需要仔细管理游标的生命周期，并确保在适当时间关闭游标以释放资源。
  
  - 资源消耗：虽然显示游标在某些情况下可以提高性能，但是在其他情况下可能会消耗更多的系统资源，例如，频繁地打开和关闭游标可能会增加数据库的开销，影响系统的整体性能。
   
- 适用场景： 需要对结果集进行复杂的操作，如逐行处理、数据转换等。
  
 
#### 隐式游标
隐式游标是数据库系统自动管理的游标。当执行SQL语句时，数据库会自动创建一个隐式游标来处理这些操作。
特点：
- 简洁性：无需显式定义和控制游标，减少了编程工作。
- 性能：由于是数据库系统自动管理，通常具有较好的性能。
- 缺乏灵活性：用户无法自定义游标的属性和行为。
 
#### 示例
- 显式游标
  ```
  --前置操作，建表和插入数据。
  gaussdb=# CREATE TABLE tb_t1(c1 int,c2 int);
  NOTICE:  The 'DISTRIBUTE BY' clause is not specified. Using 'c1' as the distribution column by default.
  HINT:  Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column.
  CREATE TABLE
  gaussdb=# INSERT INTO tb_t1 VALUES (generate_series(1,10),generate_series(1,10));
  INSERT 0 10
  --显式游标示例。
  gaussdb=# DECLARE
      v_c1 int;
      v_c2 int;
      v_res int;
      --定义游标。
      CURSOR v_cur IS SELECT * FROM tb_t1;
  BEGIN
      --打开游标。
      OPEN v_cur;
      LOOP
          --使用游标。
          FETCH v_cur INTO v_c1,v_c2;
          EXIT WHEN v_cur %notfound;
          v_res = mod(v_c1,2);  
          IF v_res = 1 THEN
              UPDATE tb_t1 SET c2 = v_c1+1 WHERE c1 = v_c1;
          END IF;
      END LOOP;
      --关闭游标。
      CLOSE v_cur; 
  END;
  /
  ANONYMOUS BLOCK EXECUTE
  --查看表结果。
  gaussdb=# SELECT * FROM tb_t1;
   c1 | c2 
  ----+----
    1 |  2
    2 |  2
    3 |  4
    4 |  4
    5 |  6
    6 |  6
    7 |  8
    8 |  8
    9 | 10
   10 | 10
  (10 rows)
  --删除表。
  gaussdb=# DROP TABLE tb_t1;
  DROP TABLE
  ```
  
- 隐式游标
  ```
  --前置操作，创建表。
  gaussdb=# CREATE TABLE tb_t1(c1 int,c2 int);
  NOTICE:  The 'DISTRIBUTE BY' clause is not specified. Using 'c1' as the distribution column by default.
  HINT:  Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column.
  CREATE TABLE
  gaussdb=# CREATE TABLE tb_t2(c1 int,c2 int);
  NOTICE:  The 'DISTRIBUTE BY' clause is not specified. Using 'c1' as the distribution column by default.
  HINT:  Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column.
  CREATE TABLE
  --隐式游标使用。
  gaussdb=# BEGIN    
      INSERT INTO tb_t1 VALUES (generate_series(1,4),generate_series(1,4));
      RAISE INFO '向tb_t1表中插入了%条数据',SQL%ROWCOUNT;
      INSERT INTO tb_t2 VALUES (generate_series(1,6),generate_series(1,6));
      RAISE INFO '向tb_t2表中插入了%条数据',SQL%ROWCOUNT;
      UPDATE tb_t1 SET c2 = c2*2 WHERE c1 > 4;
      RAISE INFO '表tb_t1中修改了数据%条',SQL%ROWCOUNT;
      UPDATE tb_t2 SET c2 = c1*2 WHERE c1 > 4;
      RAISE INFO '表tb_t2修改了数据%条',SQL%ROWCOUNT;
  END;
  /
  INFO:  向tb_t1表中插入了4条数据
  INFO:  向tb_t2表中插入了6条数据
  INFO:  表tb_t1中修改了数据0条
  INFO:  表tb_t2修改了数据2条
  ANONYMOUS BLOCK EXECUTE
  --查看表tb_t1,tb_t2的数据。
  gaussdb=# SELECT * FROM tb_t1 ORDER BY 1;
   c1 | c2 
  ----+----
    1 |  1
    2 |  2
    3 |  3
    4 |  4
  (4 rows)
  gaussdb=# SELECT * FROM tb_t2 ORDER BY 1;
   c1 | c2 
  ----+----
    1 |  1
    2 |  2
    3 |  3
    4 |  4
    5 | 10
    6 | 12
  (6 rows)
  --删除表。
  gaussdb=# DROP TABLE tb_t1;
  DROP TABLE
  gaussdb=# DROP TABLE tb_t2;
  DROP TABLE
  ```
  
 
