zoukankan      html  css  js  c++  java
  • Oracle_PL/SQL(4) 过程和函数

    create table s_sc (
    SNAME VARCHAR2(20) primary key,
    c_grade NUMBER(6),
    m_grade NUMBER(6),
    e_grade NUMBER(6)
    );
    declare
    begin
    for rs in (select sno,sname from student) loop
    insert into s_sc (sname) values (rs.sname);
    for rs2 in (select grade,cname from sc,course
    where sc.cno=course.cno and sno=rs.sno) loop
    if rs2.cname='语文' then
    update s_sc set C_GRADE=rs2.grade where sname=rs.sname;
    elsif rs2.cname='数学' then
    update s_sc set M_GRADE=rs2.grade where sname=rs.sname;
    elsif rs2.cname='英语' then
    update s_sc set E_GRADE=rs2.grade where sname=rs.sname;
    end if;
    end loop;
    end loop;
    end;

    开发子程序:过程和函数
    过程:存储过程是用于执行特定操作的PL/SQL块。特定操作主要包括对数据库表的增、删、改、查。
    如果系统或用户经常需要执行特定操作,那么可以考虑基于这些操作建立存储过程,不仅可以简化应用的开发和维护,还可以提高应用程序的运行效率。
    过程:用于对表的增、删、改、查
    函数:用于返回特定数据

    1.过程
    语法:
    create [or replace] procedure proc_name(
    argument1 [model] datatype1,arguement2 [mode2],...)
    is [as]
    变量
    begin
    语句
    end;
    关键字解释:
    create or replace:create是创建,replace是替换,
    create or replace 如果系统中不存在这个存储过程时,则创建一个新的;
    如果系统中已经存在同名的存储过程,则将原来的存储过程覆盖或替换掉。
    procedure:存储过程的关键字,类似的关键字还有trigger、function、package等。
    proc_name:存储过程名称,在同一个用户/模式下唯一,命名规则为:proc_表名_操作名。
    参数名:有意义的名字,不能是acd等,一般是列名。
    mode:参数模式,包括IN、OUT、INOUT三种类型。
    IN仅作为输入参数使用,OUT仅作为输出参数使用,INOUT即是输入参数又是输出参数。
    is …begin间:是定义变量的区域。
    begin … end间:是存储过程的主体区域,用来完成存储过程的功能。

    1.1 建立过程:不带任何参数
    第一个存储过程,无参数部分、无变量、语句部分只有null
    create or replace procedure proc_null
    is
    begin
    null;
    end proc_null;
    不能一句语句都没有
    不能重名

    练习:构造无参存储过程打印当前日期
    例1:
    create or replace procedure proc_sysdate is
    begin
    --dbms_session.set_nls('nls_date_format','"yyyy-mm-dd hh24:mi:ss"');
    dbms_output.put_line(sysdate);
    end proc_sysdate;
    /
    show error;

    1.2 调用过程
    Command窗口调用
    set serveroutput on;
    exec proc_sysdate;
    execute proc_sysdate;
    SQL窗口、Test窗口
    begin
    proc_sysdate();
    end;
    总结:
    存储过程可以被pl/sql块(包括:触发器、存储过程、包、函数、job)直接调用;
    也可以被sql通过执行命令execute(缩写exec)调用;
    ORM框架(mybais、ibatis、hibenate)及JDBC将sql语句作为字符串进行传递,
    所以sql中仍可以通过execute执行存储过程。

    1.3 建立过程:带有IN参数
    例2:
    create or replace procedure add_employee
    (p_eno number,p_name varchar2,p_sal number,
    p_job varchar2 default 'clerk',p_dno number)
    is
    begin
    insert into emp(empno,ename,sal,job,deptno) values(p_eno,p_name,p_sal,p_job,p_dno);
    end;
    /
    show err;
    exec add_employee(1111,'clark',2000,'manager',10);
    练习:编写proc_dept_add存储过程
    create or replace procedure proc_dept_add
    (p_deptno number,p_dname varchar2,p_loc varchar2)
    is
    begin
    insert into dept(deptno,dname,loc) values(p_deptno,p_dname,p_loc);
    end;
    /
    show err
    1.4 建立过程:带有OUT参数
    例3:
    create or replace procedure proc_employee
    (p_eno number,p_name out varchar2,p_salary out number)
    is
    begin
    select ename,sal into p_name,p_salary from emp where empno=p_eno;
    end;
    /
    show err;

    当在应用程序中调用该过程时,必须要定义变量接受输出参数的数据
    sql>variable name varchar2(10);
    var salary number;
    exec proc_employee(7788,:name,:salary);

    declare
    v_name varchar2(100);
    v_salary emp.sal%type;
    begin
    proc_employee(7788,v_name,v_salary);
    dbms_output.put_line(v_name||' '||v_salary);
    end;

    1.5 建立过程:带有IN OUT参数(输入输出参数)
    例4:
    create or replace procedure proc_compute
    (p_num1 in out number,p_num2 in out number)
    is
    v1 number;
    v2 number;
    begin
    v1:=trunc(p_num1/p_num2);
    v2:=mod(p_num1,p_num2);
    p_num1:=v1;
    p_num2:=v2;
    end;
    /
    show err;

    sql>variable n1 number
    var n2 number
    exec :n1:=100
    exec :n2:=30
    exec proc_compute(:n1,:n2)

    declare
    v_num1 number:=100;
    v_num2 number:=30;
    begin
    proc_compute(v_num1,v_num2);
    dbms_output.put_line('商:'||v_num1||' 余数:'||v_num2);
    end;

    1.6 为参数传递变量和数据
    位置传递,名称传递,组合传递三种
    位置传递:在调用子程序时按照参数定义的顺序为参数指定相应的变量或数值
    exec proc_dept_add(41,'开发部','北京');
    --exec proc_dept_add(42);
    exec proc_dept_add(42,null,null);
    create or replace procedure proc_dept_add
    (p_deptno number,p_dname varchar2 default '',p_loc varchar2 default '')
    is
    begin
    insert into dept(deptno,dname,loc) values(p_deptno,p_dname,p_loc);
    end;
    /
    show err;
    exec proc_dept_add(43);
    名称传递:在调用子程序时指定参数名,并使用关联符号=>为其提供相应的数值或变量
    exec proc_dept_add(p_dname=>'售后部',p_deptno=>44);
    exec proc_dept_add(p_deptno=>45);
    组合传递:同时使用位置传递和名称传递
    exec proc_dept_add(46,p_loc=>'深圳');
    exec proc_dept_add(47,p_dname=>'人事部',p_loc=>'上海');

    1.7 查看过程原代码
    oracle会将过程名,源代码以及其执行代码存放到数据字典中.执行时直接按照其执行代码执行
    可查询数据字典(user_source)
    select text from user_source where lower(name)='proc_dept_add';

    1.8 删除过程
    drop procedure proc_name;

    1.9 编译存储过程
    alter procedure proc_name compile;
    --alter table account add mark varchar2(100); 更新表结构会导致存储过程失效

    练习:
    转账业务
    create table account(
    accountid number(16),
    name varchar2(20),
    value number(16,2));
    insert into account values(111,'acdd',10000);
    insert into account values(112,'acdd1',100);

    create or replace procedure proc_trans_monery
    (p_account_out number,p_account_in number,p_value number)
    is
    begin
    Update account set value= value-p_value where accountid= p_account_out;
    Update account set value= value+p_value where accountid= p_account_in;
    end;
    exec proc_trans_monery(111,112,1000);

    做到这步基本功能是完成,但是代码里有很多bug,达不到系统使用的标准。
    首先存在的问题有:转出账户是否存在是否存在?转出账户的的金额是否够转出?转入账户是否存在?
    要解决这些问题需要增加业务校验逻辑,接下来我们改造我们的存储过程。
    create or replace procedure proc_trans_value(p_acid_out number,p_acid_in number,p_value number) is
    l_cnt number(8):=0;
    l_value account.value%type;
    begin
    select count(1) into l_cnt from account where accountid=p_acid_out;
    if l_cnt=1 then
    select value into l_value from account where accountid=p_acid_out;
    if l_value>=p_value then
    update account set value=value-p_value where accountid=p_acid_out;
    else
    raise_application_error(-20001,'[转出账户金额不足]');
    end if;
    else
    raise_application_error(-20001,'[转出账户不存在]');
    end if;

    select count(1) into l_cnt from account where accountid=p_acid_in;
    if l_cnt=1 then
    update account set value=value+p_value where accountid=p_acid_in;
    else
    raise_application_error(-20001,'[转入账户不存在]');
    end if;
    end proc_trans_value;

    用户自定义异常
    raise_application_error(-20001,'[转入账户不存在]');
    参数1:错误号,范围:-20000到-20999
    参数2:错误消息

    作业
    1.扩展转账业务,增加一个操作日志表(序号,账户,操作,金额,时间),
    当账户表发生改变时增加操作日志表信息。
    2.按照部门(scott用户下dept表)经营情况给职员(scott用户下emp表)调薪,
    调薪幅度为ACCOUNTING 5%, RESEARCH 7%, SALES 8%, OPERATIONS 6%.


    2.函数
    用于返回特定的值
    语法:
    create [or replace] function func_name
    (argument1 [mode1] datatype1,
    argument2 [mode2] datatype2,
    .....) return datatype --函数头部必须要带有RETURN子句,至少要包含一条RETURN语句
    is|as
    pl/sql block;

    2.1 建立函数:不带任何参数
    例1:
    create or replace function func_user return varchar2
    is
    v_user varchar2(100);
    begin
    select username into v_user from user_users;
    return v_user;
    end;
    /

    create or replace function func_userid return number
    is
    v_userid number;
    begin
    select user_id into v_userid from user_users;
    return v_userid;
    end;
    /
    2.2 函数调用
    使用变量接受函数返回值
    在Command窗口调用
    sql>var v1 varchar2(100);
    exec :v1:=func_user;
    print v1;
    在SQL语句中直接调用函数
    select func_user from dual;
    使用DBMS_OUTPUT调用函数
    set serveroutput on;
    exec dbms_output.put_line('当前数据库用户:'||func_user);
    总结:函数主要是通过SQL进行调用的。

    2.3 建立函数:带有IN参数
    例2:
    create or replace function func_emp_sal(p_ename in varchar2) return number
    is
    v_result emp.sal%type;
    begin
    select sal into v_result from emp where ename=upper(p_ename);
    return v_result;
    end func_emp_sal;
    /
    show error;
    --在函数中select into语句不会报未找到数据的错误。
    select func_emp_sal('scott') from dual;
    select ename,func_emp_sal(ename) from emp;

    练习:用函数实现输入部门号输出部门名。
    select func_dname(10) from dual;
    select emp.*,func_dname(deptno) dname from emp;

    2.4 建立函数:带有out参数
    例3:
    create or replace function func_dept_dname(p_ename varchar2,
    p_job out varchar2) return varchar2
    as
    v_dname dept.dname%type;
    begin
    select a.job,b.dname into p_job,v_dname from emp a,dept b
    where a.deptno=b.deptno and upper(a.ename)=upper(p_ename);
    return v_dname;
    end;
    /
    show err;
    不能使用SQL调用
    sql>var job varchar2(20);
    --select func_dept_dname('scott',:job) from dual;
    在command窗口内调用
    sql>var job varchar2(20);
    var dname varchar2(20);
    exec :dname:=func_dept_dname('scott',:job);
    在匿名块内调用
    declare
    v_dname dept.dname%type;
    v_job emp.job%type;
    begin
    v_dname:=func_dept_dname('scott',v_job);
    dbms_output.put_line('部门名:'||v_dname||' 职位:'||v_job);
    end;

    --输出2个值
    create or replace function func_dname_job2(
    p_ename in varchar2) return varchar2
    is
    v_rs varchar2(100);
    begin
    select dname||','||job into v_rs from emp,dept
    where emp.deptno=dept.deptno and ename=upper(p_ename);
    return v_rs;
    end func_dname_job2;
    /
    show error;

    --全部输出
    select func_dname_job2('scott') str from dual;
    --取分隔符位置
    select instr(func_dname_job2('scott'),',') from dual;
    --取部门信息
    select substr(str,1,instr(str,',')-1)
    from (select func_dname_job2('scott') str from dual);
    --取职位信息
    select substr(str,instr(str,',')+1)
    from (select func_dname_job2('scott') str from dual);


    练习:拼接同一个部门下的职员名
    create or replace function func_emp_ename(p_deptno number) return varchar2
    is
    v_ename varchar2(100):='';
    begin
    for rs in (select ename from emp where deptno=p_deptno) loop
    v_ename:=v_ename||' '||rs.ename;
    end loop;
    v_ename:=substr(v_ename,2);
    return v_ename;
    end;
    /

    2.5 建立函数:带有IN OUT参数
    例4:
    create or replace function func_inout(
    p_num1 number,p_num2 in out number) return number
    is
    v_rs number;
    begin
    v_rs:=trunc(p_num1/p_num2);
    p_num2:=mod(p_num1,p_num2);
    return v_rs;
    end func_inout;
    /
    show error;
    在command窗口内调用
    sql>var result1 number;
    var result2 number;
    exec :result2:=30;
    exec :result1:=func_result(100,:result2);
    在匿名块内调用
    declare
    v_shang number;
    v_yushu number;
    begin
    v_yushu:=5;
    v_shang:=func_inout(11,v_yushu);
    dbms_output.put_line('商:'||v_shang||' 余数:'||v_yushu);
    end;

    2.6 查看函数源代码
    oracle会将函数名及其源代码信息存放到数据字典中user_source
    set pagesize 40
    select text from user_source where lower(name)='func_result';

    2.7.删除函数
    drop function func_name;

    2.8 编译函数
    alter function func_name compile;

    2.9 函数调用限制
    SQL只能调用带有输入参数,不能带有输出,输入输出函数
    SQL不能调用使用返回值是PL/SQL特有数据类型(boolean,table,record等)的函数
    SQL语句中调用的函数不能包含INSERT,UPDATE和DELETE语句

    作业:
    年份转大写:要求2017年输出“二0一七”
    月份转大写:要求9月输出“九”,12月输出“十二”
    日转大写: 要求9日输出“九”,31日输出“三十一”
    金额转大写:要求10234567.89 输出“壹仟零贰拾叁万肆仟伍佰陆拾柒圆零捌角九分”

    3. 管理子程序
    3.1 列出当前用户的子程序
    数据字典视图USER_OBJECTS用于显示当前用户所包含的所有对象.
    (表,视图,索引,过程,函数,包)
    sql>col object_name format a20
    select object_name,created,status from user_objects
    where lower(object_type) in ('procedure','function');

    3.2 列出子程序源代码
    select text from user_source where name='PROC_DEPT_ADD';

    3.3 列出子程序编译错误
    使用SHOW ERRORS命令确定错误原因和位置
    show errors procedure PROC_DEPT_ADD

    使用数据字典视图USER_ERRORS确定错误原因和位置
    col text format a50
    select line||'/'||position as "line/col",text error from user_errors
    where name='PROC_DEPT_ADD';

    3.4 列出对象依赖关系
    使用数据字典视图USER_DEPENDENCIES确定直接依赖关系
    select name,type from user_dependencies where referenced_name='EMP';

    使用工具视图DEPTREE和IDEPTREE确定直接依赖和间接依赖关系
    先运行SQL脚本UTLDTREE.SQL来建立这两个视图和过程DEPTREE_FILL,然后调用DEPTREE_FILL填充这两个视图
    sql>@%oracle_home% dbmsadminutldtree
    exec deptree_fill('TABLE','scott','emp')
    执行后会将直接或间接依赖于SCOTT.EMP表的所有对象填充到视图DEPTREE和IDEPTREE中.
    select nested_level,name,type from deptree;
    select * from ideptree

    3.5 重新编译子程序
    当修改了被引用对象的结构时,就会将相关依赖对象转变为无效(INVALID)状态。
    alter table emp add remark varchar2(10);
    select object_name,object_type,status from user_objects where lower(status)='invalid';
    为了避免子程序的运行错误,应该重新编译这些存储对象
    alter procedure add_employee compile;
    alter view dept10 compile;
    alter function FUNC_EMP_SAL compile;

  • 相关阅读:
    微博转发的内容如何实现点击人名跳转到个人主页
    解决json_encode中文UNICODE转码问题
    ***git自动化部署总结
    **Git本地仓库图解
    我 Git 命令列表 (1)【转】
    Git
    git pull使用【转】
    git merge简介【转】
    获得内核函数地址的四种方法
    【笔记】一些linux实用函数技巧【原创】
  • 原文地址:https://www.cnblogs.com/BradMiller/p/9279586.html
Copyright © 2011-2022 走看看