• Oracle----存储过程


    存储过程

    基本结构

      Oracle存储过程包含三部分:过程声明,执行过程部分,存储过程异常(可写可不写,要增强脚本的容错性和调试的方便性那就写上异常处理)

    无参存储过程

    • 这里的as和is一样任选一个,在这里没有区别,其中demo是存储过程名称。
    CREATE OR REPLACE PROCEDURE demo AS/IS
    	变量2 DATE;
    	变量3 NUMBER;
    BEGIN
    	--要处理的业务逻辑
    END 

    有参存储过程

    下面脚本中,

    • 第1行:param1 是参数,类型和student表id字段的类型一样。
    • 第3行:声明变量name,类型是student表name字段的类型(同上)。
    • 第4行:声明变量age,类型数数字,初始化为20
    CREATE OR REPLACE PROCEDURE 存储过程名称(param1 student.id%TYPE)
    AS/IS
    name student.name%TYPE;
    age number :=20;
    BEGIN
      --业务处理.....
    END
    

    带参数的存储过程并且进行赋值

    下面脚本中:

    • 其中参数IN表示输入参数,是参数的默认模式。
    • OUT表示返回值参数,类型可以使用任意Oracle中的合法类型。
    • OUT模式定义的参数只能在过程体内部赋值,表示该参数可以将某个值传递回调用他的过程
    • IN OUT表示该参数可以向该过程中传递值,也可以将某个值传出去
    • 第7行:查询语句,把参数s_age作为过滤条件,INTO关键字,把查到的结果赋给total变量。
    • 第8行:输出查询结果,在数据库中“||”用来连接字符串
    • 第9—11行:做异常处理
    CREATE OR REPLACE PROCEDURE 存储过程名称(
           s_no in varchar,
           s_name out varchar,
           s_age number) AS
    total NUMBER := 0;
    BEGIN
      SELECT COUNT(1) INTO total FROM student s WHERE s.age=s_age;
      dbms_output.put_line('符合该年龄的学生有'||total||'人');
      EXCEPTION
        WHEN too_many_rows THEN
        DBMS_OUTPUT.PUT_LINE('返回值多于1行');
    END;
    

      

    存储过程语法

    这里s,m,n是变量,类型是number;

    分类

    运算符

    含义

    示例表达式

     

     

     

    算术运算符

    +

    s := 2 + 2;

    -

    s := 3 – 1;

    *

    s := 2 * 3;

    /

    s := 6 / 2;

    mod(,)

    取模,取余

    m : = mod(3,2)

    **

    乘方

    10**2 =100

     

     

     

    关系运算符

    =

    等于

    s = 2

    <>或!=或~=

    不等于

    s != 2

    <

    小于

    s < 3

    >

    大于

    s > 0

    <=

    小于等于

    s <= 9

    >=

    大于等于

    s >= 1

     

     

    比较运算符

    LIKE

    满足匹配为true

    ‘li’ like ‘%i’返回true

    BETWEEN

    是否处于一个范围中

    2 between 1 and 3 返回true

    IN

    是否处于一个集合中

    ‘x’ in (‘x’,’y’) 返回true

    IS NULL

    判断变量是否为空

    若:n:=3,n is null,返回false

     

    逻辑运算符

    AND

    逻辑与

    s=3 and c is null

    OR

    逻辑或

    s=3 or c is null

    NOT

    逻辑非

    not c is null

     

    其他

    :=

    赋值

    s := 0;

    ..

    范围

    1..9,即1至9范围

    ||

    字符串连接

    ‘hello’||’world’

    存储过程语法

    IF..END IF

    IF s_sex=1 THEN
      dbms_output.put_line('这个学生是男生');
    END IF

    IF..ELSE..END IF

    IF s_sex=1 THEN
      dbms_output.put_line('这个学生是男生');
    ELSE
      dbms_output.put_line('这个学生是女生');
    END IF
    

    循环语句

    LOOP
      IF 表达式 THEN
        EXIT;
      END IF
    END LOOP;
    

    while循环

    WHILE 表达式 LOOP
      dbms_output.put_line('haha');
    END LOOP;
    

    for循环

    FOR a in 10 .. 20 LOOP
      dbms_output.put_line('value of a: ' || a);
    END LOOP;
    

    常用的游标属性

    属性

    描述

    %FOUND

    如果DML语句执行后影响有数据被更新或DQL查到了结果,返回true。否则,返回false。

    %NOTFOUND

    如果DML语句执行后影响有数据被更新或DQL查到了结果,返回false。否则,返回true。

    %ISOPEN

    游标打开时返回true,反之,返回false。

    %ROWCOUNT

    返回DML执行后影响的行数。

    使用游标

    #声明游标定义游标的名称和相关的SELECT语句:
    CURSOR cur_cdd IS SELECT s_id, s_name FROM student;
    #打开游标游标分配内存,使得它准备取的SQL语句转换成它返回的行:
    OPEN cur_cdd;
    #抓取游标中的数据,可用LIMIT关键字来限制条数,如果没有默认每次抓取一条:
    FETCH cur_cdd INTO id, name ;
    #关闭游标来释放分配的内存:
    CLOSE cur_cdd;
    

    pl/sql处理存储过程

    新建存储过程:右键procedures,点击new,弹出PROCEDURE框,再点击OK,如下图:

    在下面的编辑区,编写存储过程脚本

    这里我们编写一个demo_cdd存储过程,要求输出“hello world”,如下图:

    右键刚才新建的存储过程名称,点击“Test”,在点击执行按钮

    pl/sql调试存储过程

    首先,当前这个用户得有能调试存储过程的权限,如果没有的话,以数据库管理员身份给你这个用户授权:

    GRANT DEBUG ANY PROCEDURE,DEBUG CONNECT SESSION TO username;

    (1).右键一个存储过程名称,点击测试(TEST),如下图:

    这里我用的pl/sql是12.0.4版本的,下面截图中与低版本的pl/sql按钮位置都相同,只是图标不一样。

     

     (2).点击两次step into按钮,进入语句调试,如下图:

     (3).每点击一次step into按钮,会想下执行一条语句,也可以查看变量和表达式的值,如下图:

    • 查看变量值:在查看变量区域,在Variable列手动输入变量i,在Value列点击下,该变量的值就显示出来了。

     

    实战

      有表student(s_no, s_name, s_age, s_grade),其中s_no-学号,也是主键,是从1开始向上排的(例如:第一个学生学号是1,第二个是2,一次类推);s_name-学生姓名;s_age-学生年龄;s_grade-年级;这张表的数据量有几千万甚至上亿。一个学年结束了,我要让这些学生全部升一年级,即,让s_grade字段加1。

    这条sql,写出来如下:

    update student set s_grade=s_grade+1
    

    分析:

      如果我们直接运行运行这条sql,因数据量太大会把数据库undo表空间撑爆,从而发生异常。那我们来写个存储过程,进行批量更新,我们每10万条提交一次。

    CREATE OR REPLACE PROCEDURE process_student is
    total NUMBER := 0;
    i NUMBER := 0;
    BEGIN
      SELECT COUNT(1) INTO total FROM student;
      WHILE i<=total LOOP
        UPDATE student SET grade=grade+1 WHERE s_no=i;
        i := i + 1;
        IF i >= 100000 THEN
          COMMIT;
        END IF;
      END LOOP;
      dbms_output.put_line('finished!');
    END;

    直接执行存储过程

      如果需要将下面脚本转成存储过程存放到数据库中,直接将 declare 删除。就行

    删除所有用户的表/视图/等

    declare
        v_name all_tables.table_name%type;
        cursor mycur is select table_name from all_tables where owner='TICM1';
    begin
        open mycur;
        loop
            fetch mycur into v_name;
            exit when mycur%NOTFOUND OR mycur%NOTFOUND IS NULL;
            execute immediate 'drop table '|| v_name;
        end loop;
        close mycur;
    end;
    
    declare
        v_name all_views.view_name%type;
        cursor mycur is select view_name from all_views where owner='ticm1';
    begin
        open mycur;
        loop
            fetch mycur into v_name;
            exit when mycur%NOTFOUND OR mycur%NOTFOUND IS NULL;
            execute immediate 'drop view '|| v_name ;
        end loop;
        close mycur;
    end;
    
    declare
        v_name ALL_SEQUENCES.SEQUENCE_NAME%type;
        cursor mycur is select SEQUENCE_NAME from ALL_SEQUENCES where SEQUENCE_OWNER='TICM1';
    begin
        open mycur;
        loop
            fetch mycur into v_name;
            exit when mycur%NOTFOUND OR mycur%NOTFOUND IS NULL;
            execute immediate 'drop SEQUENCE  '|| v_name ;
        end loop;
        close mycur;
    end;
    
    
    declare
        v_name ALL_PROCEDURES.PROCEDURE_NAME%type;
        cursor mycur is select OBJECT_NAME from ALL_PROCEDURES where OWNER='TICM1';
    begin
        open mycur;
        loop
            fetch mycur into v_name;
            exit when mycur%NOTFOUND OR mycur%NOTFOUND IS NULL;
            execute immediate 'drop PROCEDURE  '|| v_name ;
        end loop;
        close mycur;
    end;

    将上面脚本变成存储过程

    create PROCEDURE test1
    AS
        v_name ALL_PROCEDURES.PROCEDURE_NAME%type;
        cursor mycur is select OBJECT_NAME from ALL_PROCEDURES where OWNER='TICM1';
    BEGIN
        open mycur;
        dbms_output.put_line('open:');
        loop
            fetch mycur into v_name;
             dbms_output.put_line('fetch:');
            IF mycur%NOTFOUND OR mycur%NOTFOUND IS NULL THEN
                dbms_output.put_line('退出:');
                exit;
            ELSE
              execute immediate 'drop PROCEDURE  '|| v_name;
            END IF;
        end loop;
        close mycur;
    END;
    

      

      

  • 相关阅读:
    日志类
    sql查询数据并导出问题
    高并发系统设计(十七):【系统架构】微服务化后,系统架构要如何改造?
    高并发系统设计(十五):【消息队列】如何降低消息队列系统中消息的延迟?
    高并发系统设计(十四):【消息队列】如何消息不丢失?并且保证消息仅仅被消费一次?
    高并发系统设计(十三):消息队列的三大作用:削峰填谷、异步处理、模块解耦
    高并发系统设计(十二):【缓存的正确使用姿势】缓存穿透了怎么办?如何最大程度避免缓存穿透
    高并发系统设计(十一):【缓存的正确使用姿势】缓存如何做到高可用?
    ThinkPad X1 Carbon无法识别第二屏幕
    如何设置两个TPLink路由器桥接
  • 原文地址:https://www.cnblogs.com/yanxiaoge/p/14113946.html
Copyright © 2020-2023  润新知