zoukankan      html  css  js  c++  java
  • 游标(cursor)--显式游标&隐式游标、游标四个属性、循环遍历

    https://blog.csdn.net/qq_36743482/article/details/79354036

    1.1 cursor是什么
    cursor是光标,游标的意思。比如我们的鼠标的光标就是cursor。
    那么在数据库中cursor是什么呢?
    当运行DML(select,update,insert,delete)语句时,ORACLE会在内存中为其分配缓冲区(Context Area),PL/SQL打开一个内建游标并处理结果,游标是维护查询结果的内存中的一个区域。
    游标在运行DML语句时打开,完成后关闭。
    通俗的说:
    我们知道,select语句会产生一个结果集,而游标是指在这个结果集上的第一条记录的一个指针。
    而我们每次取出(fetch)一条记录,cursor就会自动指向下一条记录。
    如果学过Java的话,可以这样理解,cursor类似于Java中的迭代器iterator。

    1.2 cursor作用
    基于以上的理解,cursor自然就被用于:取出每一条记录,遍历结果集。

    1.3 cursor的四个属性
    cursor有如下四个属性:

    %isopen:布尔类型。判断游标是否打开,打开为true。对于隐式游标而言,这个值总是false,因为隐式游标在DML语句执行时打开,结束时就立即关闭。
    %found:布尔类型。在执行任何DML语句前SQL%FOUND和SQL%NOTFOUND的值都是NULL。在执行DML语句后,SQL%FOUND的属性值将是:

      . TRUE :INSERT
      . TRUE :DELETE和UPDATE,至少有一行被DELETE或UPDATE.
      . TRUE :SELECT INTO至少返回一行

    %notfound:布尔类型。当SQL%FOUND为TRUE时,SQL%NOTFOUND为FALSE

    %rowcount:数值类型。在执行任何DML语句之前,SQL%ROWCOUNT的值都是NULL,对于SELECT INTO语句,如果执行成功,SQL%ROWCOUNT的值为1,如果没有成功,SQL%ROWCOUNT的值为0,同时产生一个异常NO_DATA_FOUND。
    注:PLSQL中的布尔类型的值为null、true、false。

    2.显式游标和隐式游标
    在游标声明之前,我们来看下显式游标和隐式游标。

    2.1 隐式游标
    事实上,当我们在PLSQL中进行非查询(或者返回单条记录的查询)语句,如update、delete、insert等时,ORACLE 系统会自动地为这些操作设置游标并创建其工作区,并且隐式游标的名字为SQL,由ORACLE 系统定义。
    对于隐式游标的操作,如定义、打开、取值及关闭操作,都由ORACLE 系统自动地完成,无需用户进行处理。
    PL/SQL管理隐式游标,当查询开始时隐式游标打开,查询结束时隐式游标自动关闭。
    用户只能通过隐式游标的相关属性,来完成相应的操作。在隐式游标的工作区中,所存放的数据是与用户自定义的显示游标无关的、最新处理的一条SQL 语句所包含的数据。
    简单实例
    对于如下一张表ljb_test,更新每个人的薪水:


    set serveroutput on;
    begin
    update ljb_test set salary = salary + 1;
    dbms_output.put_line('更新了'||SQL%rowcount||'行数据'); --must before commit
    commit;
    end;
    /
    1
    2
    3
    4
    5
    6
    7
    结果如下:


    这里需要指出的是:关于隐式游标的属性操作,必须在commit之前,可以尝试把打印输出放在commit之后,得到的结果是0。这里之所以结果加了2,是因为我实验了两次。

    2.2 显式游标
    当查询返回结果超过一行时,就需要一个显式游标,此时用户不能使用select into语句。
    显式游标在PL/SQL块的声明部分声明,在执行部分或异常处理部分打开,取数据,关闭。
    这里要做一个声明,我们所说的游标通常是指显式游标,而显式游标需要被声明。

    2.2.1 cursor的声明、打开、关闭、从游标提取数据
    声明游标

    CURSOR cursor_name IS select_statement;
    1
    打开游标

    OPEN cursor_name;
    1
    关闭游标

    CLOSE cursor_name;
    1
    从游标提取数据
    从游标得到一行数据使用FETCH命令。每一次提取数据后,游标都指向结果集的下一行。
    语法如下:

    FETCH cursor_name INTO variable[,variable,...]
    1
    如:

    set serveroutput on;
    declare
    cursor c is select * from ljb_test; --1.声明游标的时候Oracle不会从数据库中取数据
    v_test c%rowtype;
    begin
    open c; --2.打开游标,此时从数据库中取数据,并把结果集放在内存中
    fetch c into v_test; --3.获取数据,fetch的时候游标自动往下移动一格
    dbms_output.put_line(v_test.name);

    fetch c into v_test;
    dbms_output.put_line(v_test.name);
    close c; --4.关闭游标,清掉内存。成对编程
    end;
    /
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    命令行运行,结果如下:


    2.2.2 遍历结果集
    如果我们想要遍历整个测试表,显然需要通过循环来进行。
    正常情况下如果遵循循环遍历游标应当遵从以下步骤:
    1、打开游标
    2、开始循环
    3、从游标中取值
    4、检查那一行被返回
    5、处理
    6、关闭循环
    7、关闭游标
    事实上,我们确实可以通过该方式实现,即通过while循环和do while循环。
    但是,for循环却不无需这么复杂,这里我们重点介绍for循环。
    上文提到,PLSQL中有三种循环,这里需要指出的是,使用游标遍历时,最简单最稳定的就是for循环,但另外两种循环仍然会做简单介绍。如下:

    2.2.2.1 for循环遍历
    FOR循环的游标按照正常的声明方式声明,但是不需要显式的打开、关闭、取数据,测试数据的存在、定义存放数据的变量等等。
    for循环是最简单也是最不容易出错的方式,推荐采用for循环遍历。

    set serveroutput on;
    declare
    cursor c is select * from ljb_test;
    --无需在此声明变量v_test
    begin
    --无需显式打开游标
    for v_test in c loop
    --无需显式fetch
    dbms_output.put_line(c%rowcount||'--'||v_test.name);
    end loop;
    --无需显式关闭游标
    end;
    /
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    九条记录全部被打印,运行结果如下:


    2.2.2.2 while循环遍历
    declare
    cursor c is select * from ljb_test; --声明游标的时候Oracle不会从数据库中取数据
    v_test c%rowtype;
    begin
    open c; --打开游标,此时从数据库中取数据,并把结果集放在内存中

    fetch c into v_test; --获取数据,fetch的时候游标自动往下移动一格
    while c%found loop
    dbms_output.put_line(c%rowcount||'--'||v_test.name);
    fetch c into v_test;
    end loop;
    close c; --关闭游标,清掉内存。成对编程
    end;
    /
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    九条记录被遍历,结果如下:


    如果我们把fetch语句和打印语句调换一下位置,结果会怎样?


    可以看到,第一个记录被跳过取,最后一个打印两边。原因是,我们第一次打印前fetch了两次,而最后一个虽然c无法fetch到数据,但是上一个的c%fetch仍然是true。

    所以,采用while循环一定注意fetch和打印的顺序。do while类似。

    2.2.2.3 do while循环遍历
    declare
    cursor c is select * from ljb_test; --声明游标的时候Oracle不会从数据库中取数据
    v_test c%rowtype;
    begin
    open c;
    loop
    fetch c into v_test;
    exit when(c%notfound);
    dbms_output.put_line(c%rowcount||'--'||v_test.name); --如果顺序反了,就会最后一条记录打印两次
    end loop;
    close c; --关闭游标,清掉内存。成对编程
    end;
    /
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13


    2.2.3 含参游标
    类似于函数,我们可以将参数传递给游标并在查询中使用。

    CURSOR cursor_name[(parameter[,parameter],...)]
    IS select_statement;
    1
    2
    参数定义方式为:

    Parameter_name [IN] data_type[{:=|DEFAULT} value]
    1
    需要注意的是:游标只能接受传递的值,而不能返回值。参数只定义数据类型,没有大小。

    declare
    cursor c(v_dep ljb_test.dep%type, v_salary ljb_test.salary%type)
    is select * from ljb_test where dep = v_dep and salary = v_salary;
    begin
    for v_temp in c(3,4000) loop
    dbms_output.put_line(v_temp.name);
    end loop;
    end;
    /
    1
    2
    3
    4
    5
    6
    7
    8
    9
    结果如下:


    2.4 可更新游标
    declare
    cursor c is select * from ljb_test for update; --添加for update即可
    begin
    for v_temp in c loop
    if(v_temp.salary<3500) then
    update ljb_test set salary = salary * 2 where current of c; --更新条件
    elsif(v_temp = 5000) then
    delete from ljb_test where current of c; --更新条件
    end if;
    end loop;
    commit;
    end;
    /
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    ————————————————
    版权声明:本文为CSDN博主「刘金宝_Arvin」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_36743482/java/article/details/79354036

  • 相关阅读:
    Linux命令应用大词典-第11章 Shell编程
    Kubernetes 学习12 kubernetes 存储卷
    linux dd命令
    Kubernetes 学习11 kubernetes ingress及ingress controller
    Kubernetes 学习10 Service资源
    Kubernetes 学习9 Pod控制器
    Kubernetes 学习8 Pod控制器
    Kubernetes 学习7 Pod控制器应用进阶2
    Kubernetes 学习6 Pod控制器应用进阶
    Kubernetes 学习5 kubernetes资源清单定义入门
  • 原文地址:https://www.cnblogs.com/kakaisgood/p/12675132.html
Copyright © 2011-2022 走看看