• PL/SQL


    投简历好多要求会存储过程、触发器的,虽然工作中没有用到过,但是学一下吧,以备不时之需。记性不好,在此记录一下,有错误的希望批评指正。

    (SQL Server的扩展叫Transact-SQL)

    先看一Hello World例子:用oracle的sql plus(最好以管理员身份运行)用scott/tiger用户连接上oracle,先随便执行个select,再输入ed,会打开Oracle自带的一个afiedt.buf的编辑器,这个好处就是当你的多行sql有错误时,可以直接再编辑所有的sql,编辑完ctrl+s保存,到命令窗口直接输入 / (/表示执行上一条sql或者PLSQL程序)就能执行编辑器的sql:

    declare
        --变量的说明
    begin
        --程序体
        --调用内置程序包,打印
        dbms_output.put_line('Hello World');
    end;
    /

    2 如果需要在屏幕上输出信息,需要将serveroutput开关打开,,默认是off的

                Set   serveroutput   on   

    3,说明变量,类型有以下

    变量和常量:

    说明变量(char,varchar2,date,number,boolean,long)

    varl           char(15);      --变量名  类型 长度 分号结束

    married boolean :=true;   

    psal                 number(7,2);

    my_name        emp.ename%type;    --引用型变量,表示my_name的类型和emp表中的ename列的类型一样。  

    emp_rec         emp%rowtype;    -- 记录型变量,取emp表一行的类型作为类型,类似于java的类   取一行的一列:emp_rec.ename=’ADMIN’

    例子:

     查询并7369的姓名和薪水:

    用引用型变量实现:

    declare
               --定义变量
        pname  emp.ename%type;
        psal   emp.sal%type;
    begin
               --查询
        select ename,sal  into  pname,psal  from  emp  where empno=7369;
               --打印
        dbms_output.put_line(pname||' 的薪水是 '|| psal);
    end;
    /

    用记录型变量实现:

    declare
        emp_rec emp%rowtype;
    begin
        select * into emp_rec from emp where empno=7369;
        dbms_output.put_line(emp_rec.ename||' 的薪水是 '|| emp_rec.sal);
    end;
    /

    IF语句:

    一:

      IF条件  THEN 语句1;

      语句2;

      END IF;

    二:

      IF  条件  THEN  语句序列1;

            ELSE  语句序列2;

            END  IF;

    三:  

      IF  条件  THEN  语句;

            ELSIF  语句  THEN 语句;

            ELSE  语句;

          END IF;

    例子:

    declare 
        pnum number:= 45;
    begin
        --判断 
        if pnum <=20 then 
            dbms_output.put_line('青少年');
        elsif pnum <=30  then
            dbms_output.put_line('青年');
        elsif pnum <= 50 then
            dbms_output.put_line('中年');
        else 
            dbms_output.put_line('中老年');
        end if;
    end;
    /

    循环语句:

     一:while循环

      While  total <= 1000

      LOOP

          Total := total + salary;

      End loop;

    二:do  while循环,至少执行一次

      Loop

        Exit[when  条件];

        ……

      End  loop;

    三:

      For  I in  1..3

      Loop

        语句序列;

      End  loop;

      ///可以这么用:循环14次

      Fro  pename in (select  ename  from emp)

      Loop

      End   loop;

    循环例子:--打印1-10

    While循环:

    --打印1-10
    declare 
        pnum  number := 1;
    begin
        while pnum <=10
        loop
             dbms_output.put_line(pnum);
           pnum := pnum+1;
        end loop;
    end;
    /

    Loop循环:

    declare 
        pnum  number := 1;
    begin
        loop
           --条件成立退出
           exit when pnum >10;
           
    
           --隐式转换 put_line 参数是varchar
             dbms_output.put_line(pnum);
    
           pnum := pnum+1;
    
        end loop;
    end;
    /

    Fro循环:

    --打印1-10
    declare 
        pnum  number := 1;
    begin
        for pnum in 1..10
        loop
             dbms_output.put_line(pnum);
        end loop;
    end;
    /

    光标:cursor,相当于jdbc的ResultSet

    光标的属性:

      %isopen  是否被打开 boolean

      %rowcount行数

      %notfound  是否有值

     光标打开不关闭会造成内存溢出,所以oracle做了限制,默认情况只能打开300个光标

    例子:使用游标查询员工姓名和工资,并打印:

    
    
    --光标: 使用游标查询员工姓名和工资,并打印
    
    /*
    光标的属性:
    %isopen 是否被打开
    %rowcount 行数
    %notfound 是否有值
    */
    
    set serveroutput on
    
    declare
      --光标
      cursor cemp is select ename,sal from emp;
      pename emp.ename%type;
      psal   emp.sal%type;
    begin
      open cemp;
      loop
        --从集合中取值
        fetch cemp into pename,psal;
        --****
        exit when cemp%notfound;
        
        dbms_output.put_line(pename||'的薪水是'||psal);
    
      end loop;
      close cemp;
    end;
    /
    
    

    给员工涨工资: 总裁涨1000  经理涨800  其他人涨400:(emp2是我把emp  copy了一份)

    --给员工涨工资  总裁1000 经理800 其他400
    
    set serveroutput on
    /*
    SQL> show parameters cursor
    
    NAME                                 TYPE        VALUE
    ------------------------------------ ----------- --------
    cursor_sharing                       string      EXACT
    cursor_space_for_time                boolean     FALSE
    open_cursors                         integer     300
    session_cached_cursors               integer     20
    
    */
    declare
      --光标代表员工
      cursor cemp is select empno,job from emp2;
      pempno emp2.empno%type;
      pjob   emp2.empjob%type;
    begin
      --rollback;
      open cemp;
      loop
        fetch cemp into pempno,pjob;
        exit when cemp%notfound;
        --判断
        if pjob = 'PRESIDENT' then update emp2 set sal=sal+1000 where empno=pempno;
          elsif pjob = 'MANAGER' then update emp2 set sal=sal+800 where empno=pempno;
          else update emp2 set sal=sal+400 where empno=pempno;
        end if;
      end loop;
      close cemp;
      --提交: 隔离级别
      commit;
      dbms_output.put_line('完成');
    end;
    /
     
    总结光标书写顺序:


    上边说的是不带参数的光标,下边说说带参数的光标用法:

    带参数的光标:

    类似于形参、实参的概念,open光标的时候传进去实参,声明的时候是形参

    --带参数的光标:查询某个部门的员工姓名 
    
    set serveroutput on
    
    declare
      cursor cemp(pdno number) is  select ename from emp where deptno=pdno;
      pename emp.ename%type;
    begin
      open cemp(20);
      loop
        fetch cemp into pename;
        exit when cemp%notfound;
        
        dbms_output.put_line(pename);
    
    
      end loop;
      close cemp;
    end;
    /

    例外、异常:

    Oracle的命名规则,如dbms_output.put_line,单词之间使用下划线分割,变量使用v_name,  游标可以使用c_emp, 等等

    例子:除数是0:

    /*
    zero_divide  被零除
    */
    declare
        v_num number;
    begin 
        v_num := 1/0;
    exception
        --then 后可以有多条语句
        when  zero_divide  then dbms_output.put_line('1:除数不能是零');
                    dbms_output.put_line('2:除数不能是零');
        when  others  then dbms_output.put_line('其他例外');
    end;
    /

    自定义例外:

     把自定义例外当做变量,在declare里声明,抛出用raise

    注意:oracle 会自动关闭光标,为保险起见,手动关闭更好,在catch到异常后,判断光标是否关闭,这就用到了游标的 %isopen属性:

    if cemp%isopen then

        close no_emp_found;

      end if;

     例子:自定义例外: 查询50号部门的员工姓名

    --自定义例外: 查询50号部门的员工姓名
    
    set serveroutput on
    
    declare
      cursor cemp is select ename from emp where deptno=50;
      pename emp.ename%type;
      
      --自定义例外
      no_emp_found exception;
    begin
      open cemp;
      --取一个员工
      fetch cemp into pename;
      if cemp%notfound then 
        raise no_emp_found;
      end if;
    
    /*
      if cemp%isopen then 
        close no_emp_found;
      end if;
    */
      close cemp;
      
    exception 
      when no_emp_found then dbms_output.put_line('没有找到员工');
      when others then dbms_output.put_line('其他例外');
      
    end;
    /

    实例1:统计每年入职的员工个数。

    /*
    实例1:统计每年入职的员工个数。
    
    可能SQL:
    select to_char(hiredate,'yyyy') from emp;
    */
    set serveroutput on
    declare
      cursor cemp is select to_char(hiredate,'yyyy') from emp;
      phiredate varchar2(4);
      
      --计数器
      count80 number := 0;
      count81 number := 0;
      count82 number := 0;
      count87 number := 0;
    begin
      open cemp;
      loop
        --取一个员工
        fetch cemp into phiredate;
        exit when cemp%notfound;
        
        --判断
        if phiredate = '1980' then count80:=count80+1;
          elsif phiredate = '1981' then count81:=count81+1;
          elsif phiredate = '1982' then count82:=count82+1;
          else count87 := count87+1;
        end if;      
    
      end loop;
      close cemp;
      
      --输出
      dbms_output.put_line('total:'||(count80+count81+count82+count87));
      dbms_output.put_line('1980:'|| count80);
      dbms_output.put_line('1981:'|| count81);
      dbms_output.put_line('1982:'|| count82);
      dbms_output.put_line('1987:'|| count87);
    end;
    /

     实例2:

    为员工长工资。从最低工资调起每人长10%,但工资总额不能超过5万元,
    请计算长工资的人数和长工资后的工资总额,并输出输出长工资人数及工资总额。

    /*
    为员工长工资。从最低工资调起每人长10%,但工资总额不能超过5万元,
    请计算长工资的人数和长工资后的工资总额,并输出输出长工资人数及工资总额。
    
    可能的SQL:
    员工:  select empno,sal from emp order by sal;
    长工资后的工资总额:1. 对sal进行累加: 新的工资总额=旧的工资 + sal*0.1;
                    2. sum(sal): 查询数据库
    练习: 工资不能超过5w
    */
    set serveroutput on
    declare
      --员工
      cursor cemp is select empno,sal from emp order by sal;
            
      pempno emp.empno%type;
      psal   emp.sal%type;
      
      --长工资的人数
      countEmp number := 0;
      --工资总额
      salTotal number;
    begin
    
       --涨前工资总额
      select sum(sal) into salTotal from emp;
    
      open cemp;
      loop
        --工资总额>5w
        exit when salTotal > 50000;
        --取一个员工
        fetch cemp into pempno,psal;
    --涨工资
        update emp set sal=sal*1.1 where empno=pempno;
        exit when salTotal > 50000;
        --人数
        countEmp := countEmp +1;
        --工资总额
        salTotal := salTotal + psal * 0.1;
      end loop;
      close cemp;
      
      commit;
    
      --输出
      dbms_output.put_line('长工资的人数:'|| countEmp);
      dbms_output.put_line('工资总额:'|| salTotal);
    
    end;
    /

    实例3:操作2张表

    分析,先从deptno表取出有哪些deptno,再从emp表根据deptno取每个部门的信息,两层循环。

    /*
    用PL/SQL语言编写一程序,实现按部门分段(6000以上、(6000,3000)、3000元以下)
    统计各工资段的职工人数、以及各部门的工资总额(工资总额中不包括奖金)
    
    SQL语句:
    部门:  select deptno from dept;
    员工的工资: select sal from emp where deptno=???
    工资总额:  select sum(sal) from emp where deptno=???  
    */
    set serveroutput on
    declare
      --部门 
      cursor cdept is select deptno from dept;
      pdno dept.deptno%type;
      
      --部门中的员工
      cursor cemp(dno number) is select sal from emp where deptno=dno;
      psal emp.sal%type;
      
      --各个段的人数
      count1 number;count2 number;count3 number;
      --部门的工资总额
      salTotal number;
    begin
      open cdept;
      loop
        --取部门
        fetch cdept into pdno;
        exit when cdept%notfound;
        
        --初始化
        count1 :=0;count2:=0;count3:=0;
        select sum(sal) into salTotal  from emp where deptno=pdno;
        
        --取部门中的员工
        open cemp(pdno);
        loop
          fetch cemp into psal;
          exit when cemp%notfound;
          
          --判断
          if psal<3000 then count1:=count1+1;
            elsif psal>=3000 and psal<6000 then count2:=count2+1;
            else count3:=count3+1;
          end if;        
        end loop;
        close cemp;
        
        --保存当前部门
        insert into msg1 values(pdno,count1,count2,count3,nvl(salTotal,0));
        
      end loop;
      close cdept;
      
      commit;
      dbms_output.put_line('完成');
    
    
    
    end;
    /

    create table msg1
    (deptno number,
    emp_num1 number,
    emp_num2 number,
    emp_num3 number,
    sum_sal number);
    create tablel msg1

  • 相关阅读:
    使用docker部署微服务
    配置git仓库SSH秘钥,实现免密登录
    windows添加打印机失败
    流媒体之HLS与DASH
    docker安装mysql8.0
    去它的不要找客观原因
    idea提交代码到gitee报错:The requested URL returned error: 403
    使用docker compose微服务编排
    docker安装redis
    OpenGL环境安装
  • 原文地址:https://www.cnblogs.com/lihaoyang/p/7812926.html
Copyright © 2020-2023  润新知