Oracle 游标的工作原理(为什么可以使用先定义后赋值的变量)

SqlDemo

SET SERVEROUTPUT ON
DECLARE
  VERSION_NO_MAX INT;
  TYPE tb_table_type IS TABLE OF TABLE_NAME%rowtype;  -- 目标表 类型
  tb_table tb_table_type;  -- 目标表 集合
  CURSOR temp_cursor IS --  需要添加的数据
     SELECT * FROM TABLE_NAME
     WHERE VERSION_NO = VERSION_NO_MAX;
BEGIN
    dbms_output.put_line('VERSION_NO_MAX = ' || VERSION_NO_MAX);
    -- 获取最大版本号
    SELECT NVL(MAX(VERSION_NO), 0) + 0 INTO VERSION_NO_MAX 
    FROM TABLE_NAME;
    dbms_output.put_line('VERSION_NO_MAX = ' || VERSION_NO_MAX);
    
    -- 逻辑
    OPEN temp_cursor;
    LOOP
        -- 从 游标 中取出指定长度数据放到 内存中
        FETCH temp_cursor BULK COLLECT INTO tb_table LIMIT 10000;
    
        dbms_output.put_line('COUNT = ' || tb_table.COUNT);
    
        -- 如果没有数据则退出
        EXIT WHEN temp_cursor%notfound;
    END LOOP;
    CLOSE temp_cursor; 
END;

1. 游标定义阶段:只解析 SQL 结构

当 PL/SQL 解析器处理CURSOR temp_cursor IS ...语句时:

1.只进行语法检查和 SQL 结构解析
2.不会立即执行 SQL 语句
3.变量 VERSION_NO_MAX 在此时只是一个占位符,解析器知道它是一个变量,但不关心其值

2. 游标打开阶段:绑定变量值

当执行OPEN temp_cursor时,PL/SQL 会:

1.编译 SQL 语句
2.将变量VERSION_NO_MAX的值绑定到 SQL 中
3.此时VERSION_NO_MAX已经被赋值,所以绑定的是正确的值

3. 变量作用域与值传递

1.变量在 DECLARE 块中声明,在 BEGIN 块中赋值
2.游标定义中引用的变量,实际使用的是打开游标时该变量的当前值
3.这类似于动态 SQL 中的绑定变量机制

4. 游标执行计划与变量值

数据库执行游标 SQL 时:

1.会根据绑定的变量值生成执行计划
2.变量值的变化不会影响 SQL 的语法结构,只是改变查询条件

5.游标工作流程示意图

1. 解析阶段:
   CURSOR temp_cursor IS SELECT * FROM table WHERE VERSION_NO = :bind_var;
   (将VERSION_NO_MAX识别为绑定变量:bind_var)

2. BEGIN块执行:
   VERSION_NO_MAX = NULL;  -- 初始值
   VERSION_NO_MAX = 100;   -- 查询后赋值

3. 打开游标时:
   执行SQL: SELECT * FROM table WHERE VERSION_NO = 100;
   (将变量值100绑定到:bind_var)

6.这种写法的潜在风险

1.变量未初始化警告:PL/SQL 编译器会提示 "Variable"VERSION_NO_MAX"might not have been initialized"
2.逻辑可读性问题:先使用后赋值不符合常规编程习惯,可能让其他开发者困惑
3.意外 NULL 值风险:如果查询结果为空,VERSION_NO_MAX会被赋值为 0,但初始输出时可能显示 NULL

7.最佳实践写法

SET SERVEROUTPUT ON
DECLARE
  VERSION_NO_MAX INT;
  TYPE tb_table_type IS TABLE OF TABLE_NAME%rowtype;  -- 目标表 类型
  tb_table tb_table_type;  -- 目标表 集合
  CURSOR temp_cursor(ITEM_VERSION_NO INT) IS --  需要添加的数据
     SELECT * FROM TABLE_NAME
     WHERE VERSION_NO = ITEM_VERSION_NO;
BEGIN
    dbms_output.put_line('VERSION_NO_MAX = ' || VERSION_NO_MAX);
    -- 获取最大版本号
    SELECT NVL(MAX(VERSION_NO), 0) + 0 INTO VERSION_NO_MAX 
    FROM TABLE_NAME;
    dbms_output.put_line('VERSION_NO_MAX = ' || VERSION_NO_MAX);
    
    -- 逻辑
    OPEN temp_cursor(VERSION_NO_MAX);
    LOOP
        -- 从 游标 中取出指定长度数据放到 内存中
        FETCH temp_cursor BULK COLLECT INTO tb_table LIMIT 10000;
    
        dbms_output.put_line('COUNT = ' || tb_table.COUNT);
    
        -- 如果没有数据则退出
        EXIT WHEN temp_cursor%notfound;
    END LOOP;
    CLOSE temp_cursor; 
END;

7.1.这种写法的好处

1.符合 "先赋值后使用" 的编程习惯
2.明确游标参数,提高代码可读性
3.避免变量未初始化的警告
4.更清晰地表达游标的查询条件依赖关系

总结

PL/SQL 游标之所以能在变量先使用后赋值的情况下正常工作,是因为:

1.游标定义时只解析 SQL 结构,不执行也不关心变量值
2.游标打开时才会绑定变量的当前值
3.这本质上是一种绑定变量的机制,与动态 SQL 的工作原理类似

 

posted @ 2025-06-12 16:11  Robot-Blog  阅读(25)  评论(0)    收藏  举报