Oracle扩充PL/SQL

PL/SQL

*** PL/SQL *** 
    过程语言(Procedural Language)与结构化查询语言(SQL)结合而成的数据库编程语言
    PL提供流程控制的语句
    SQL提供访问数据库的语句
    PL/SQL是对sql的扩展  使其具有流程控制能力
    
    PL/SQL的特点:
       >支持sql的所有数据类型
       >支持对象类型和集合类型的数据
       >让sql具有选择和循环的流程控制能力
       >具有可移植性和安全性
       >可以创建函数,存储过程,触发器等  实现sql语句的复用
    PL/SQL代码组成:pl代码+sql代码 
    PL/SQL结构组成:声明部分+可执行部分+异常捕获部分
    基本格式:
    declare
         定义变量
    begin
         可执行sql语句
         exception
         异常捕获要执行的代码
    end;

选择结构

/*** -- 选择结构:单分支 ***/
declare
   resultsex char(3);
begin
   /*给变量赋值方式1*/
   resultsex:='男';
   /*给变量赋值方式2*/
   select sex into resultsex from student where tid=1;
   dbms_output.put_line('resultsex'||resultsex);
   if resultsex='男' then
      dbms_output.put_line('resultsex='||resultsex||':称呼是:大帅哥!');
   end if;
   if resultsex='女' then
      dbms_output.put_line('resultsex='||resultsex||':称呼是:小美女!');
   end if;
   
   exception
      when others then
      dbms_output.put_line('出错了!');  
end;
-----------------------------------------------------------------------
-- 选择结构:双分支
declare
   resultsex char(30);
begin
   resultsex:='&请输入性别'; /*弹出一个输入框*/
   dbms_output.put_line('resultsex='||resultsex);
   if resultsex='男' then
      dbms_output.put_line('帅哥!');
   else
      dbms_output.put_line('美女!');
   end if;

   exception
      when others then
      dbms_output.put_line('出错了!!!');
end;
-----------------------------------------------------------------------
-- 选择结构:多分支 使用if elsif
declare
   score number(4,1);
begin
   score:='&请输入分数';
   dbms_output.put_line('分数='||score);
   if score < 0 or score > 100 then
      dbms_output.put_line('分数无效!');
   elsif score < 60 then
      dbms_output.put_line('不及格!');
   elsif score < 70 then
      dbms_output.put_line('及格!');
   elsif score < 80 then
      dbms_output.put_line('良好!');
   elsif score < 100 then
      dbms_output.put_line('优秀!');
   else 
      dbms_output.put_line('满分!');
   end if;
   
   exception
      when others then
      dbms_output.put_line('出错了!!'); 
end;
-----------------------------------------------------------------------
-- 选择结构:多分枝 使用case
declare
   score number(4,1);
   scoreInt number(4,1);
begin
   score:='&请输入分数';
   scoreInt:=trunc(score/10);
   case scoreInt
      when 6 then
         dbms_output.put_line('及格!');
      when 7 then
         dbms_output.put_line('良好!'); 
      when 8 then
         dbms_output.put_line('优秀!'); 
      when 9 then
         dbms_output.put_line('优秀!'); 
      when 10 then
         dbms_output.put_line('满分!'); 
      else
         dbms_output.put_line('不及格!'); 
      end case;
      
      exception
         when others then
         dbms_output.put_line('出错了!');         
end;

练习

--1  判断输入的数是不是水仙花数
declare
    n int;
    sumn int; 
    a int; b int; c int;
begin
    /*给n赋值*/ 
    n:='&请输入一个三位数的整数:';/*&xxx获取值时  提示信息中不能有:*/
    if n>=100 and n<1000 then
       /*获取各个位数的值*/
       a:=mod(n,10);
       b:=mod(trunc(n/10),10);
       c:=trunc(n/100);
       /*获取三次方之和*/
       sumn:=power(a,3)+power(b,3)+power(c,3);
       if sumn=n then
          dbms_output.put_line(n||'是三位数,是水仙花数!');
       else
          dbms_output.put_line(n||'是三位数,但不是水仙花数!');  
       end if;        
    else 
       dbms_output.put_line(n||'不是三位数!更不是水仙花数!'); 
    end if;
    
    exception
    when others then
       dbms_output.put_line('出错了!'); 
end;
-----------------------------------------------------------------------
-- 2 根据年龄和性别 判断称呼
declare
   resultSex char(3);
   resultAge int;
   chengHu varchar(100);
begin
   resultSex:='&请输入性别:';
   resultAge:='&请输入年龄:';
   dbms_output.put(resultSex||':::'||resultAge);
   /*判断*/
   if resultSex='男' then
      if resultAge < 18 then
         chengHu:='小男孩';
      elsif resultAge < 35 then
         chengHu:='帅哥';
      elsif resultAge < 60 then
         chengHu:='叔叔';
      else
         chengHu:='爷爷';
      end if;
   else
      if resultAge < 18 then
         chengHu:='小女孩';
      elsif resultAge < 35 then
         chengHu:='美女';
      elsif resultAge < 60 then
         chengHu:='阿姨';
      else
         chengHu:='奶奶';
      end if;
   end if;
   dbms_output.put_line('->称呼是'||chengHu);
   
   exception
   when others then
      dbms_output.put_line('出错了!');
end;
-----------------------------------------------------------------------
-- 3 输入年月日 判断距离今天的天数
declare
   myYear int;
   myMonth int;
   myDay int;
   days int;
   myDate date;
begin
   myYear:='&请输入年:';
   myMonth:='&请输入月:';
   myDay:='&请输入日:';
   /*封装为date对象*/
   myDate:=to_date(myYear||'-'||myMonth||'-'||myDay,'yyyy-mm-dd');
   days:=abs(myDate-sysdate);
   dbms_output.put_line(myYear||'-'||myMonth||'-'||myDay||':距离今天的天数是:'||days);
   
   exception
   when others then
      dbms_output.put_line('出错了!');
end;
-----------------------------------------------------------------------
-- 4 判断指定年月的天数
declare
   myYear int;
   myMonth int; 
   days int;
begin
   myYear:='&请输入年:';
   myMonth:='&请输入月:';
   case myMonth
      when 4 then
         days:=30;
      when 6 then
         days:=30;
      when 9 then
         days:=30;
      when 11 then
         days:=30;
      when 2 then
         /*判断闰年*/ 
         if (mod(myYear,4)=0 and mod(myYear,100)!=0) or (mod(myYear,400)=0) then
            days:=29;
         else
            days:=28;
         end if;
      else
         days:=31;
   end case;
   dbms_output.put_line(myYear||'年'||myMonth||'月的天数是:'||days);
   
   exception
   when others then
      dbms_output.put_line('出错了!');
end;

循环结构

-- 循环结构
-- loop  while  for
-----------------------------------------------------------------------
-- 1:循环结构: loop::不带条件的循环
/*
   loop
       循环执行的代码
       if 条件  then
          exit;
       end if;   
   end loop;
*/
-----------------------------------------------------------------------
-- 打印从1到10
declare
   i int;
begin
   i:=1;
   loop
      if i > 10 then
         exit;/*结束循环*/
      end if;
      dbms_output.put_line(i); 
      i:=i+1;
   end loop;
end;
-----------------------------------------------------------------------
-- 打印99乘法表
declare
   i int:=1;
   j int:=1;
begin
   loop
       j:=1;
       loop
          if j > i then
             exit;
          end if;
          if i*j >=10 then
             dbms_output.put(j||'*'||i||'='||i*j||' ');
          else
             dbms_output.put(j||'*'||i||'='||i*j||'  ');
          end if;
          j:=j+1;
       end loop;
       if i > 9 then
          exit; 
       end if;
       i:=i+1;
       dbms_output.put_line('');
   end loop;
end;
-----------------------------------------------------------------------
--  2:循环结构: while::带条件的循环 
/*
   while 条件 loop
       循环执行的代码
   end loop;
*/
-----------------------------------------------------------------------
-- 获取1到100内的数的和
declare
   sumn int:=0;
   n int:=1;
begin
   while n<=100 loop
      sumn:=sumn+n;
      n:=n+1;
   end loop;
   dbms_output.put_line('1到100内的数的和='||sumn);
end;
-----------------------------------------------------------------------
-- 获取1到100内质数的和
declare
   sumn int:=0;
   n int:=1;
   m int;
   b boolean;
begin
   /*定义一个变量让其从1跑到100*/
   while n<=100 loop
      /*判断当前数n是不是质数*/
      m:=2;
      b:=true;
      while m<=sqrt(n) loop
         if mod(n,m)=0 then
            b:=false;
            exit;
         end if;
         m:=m+1;
      end loop;
      if b and n!=1 then
          dbms_output.put_line(n||'是质数!'); 
          sumn:=sumn+1;
      end if;
      n:=n+1;
   end loop;
   dbms_output.put_line('1到100内的质数的和='||sumn);   
end;
-----------------------------------------------------------------------
--  3:循环结构: for::带条件的循环 
/*
for n in a...b loop
    循环执行的代码
end loop;
*/
-----------------------------------------------------------------------
-- 99乘法表
begin
   for i in 1..9 loop
      for j in 1..i loop
         if i*j >=10 then
            dbms_output.put(j||'*'||i||'='||i*j||' ');
         else
            dbms_output.put(j||'*'||i||'='||i*j||'  ');
         end if;
      end loop;
         dbms_output.put_line('');
   end loop;
end;
-----------------------------------------------------------------------
=======================================================================
-- 标记使用1:结束多层循环
begin
  dbms_output.put_line('*');
  <<a>> /*在此位置定义一个标记*/
  for i in 1..9 loop
    for j in 1..9 loop
      dbms_output.put_line(i||':'||j);
      if j=5 then
        exit a; /*结束外层for循环*/
      end if;
    end loop;
  end loop;
  dbms_output.put_line('*');
end;
----------------------------------------------------------------------
-- 标记使用2:实现跳转到执行位置
begin
  dbms_output.put_line('*');
    for i in 1..9 loop
      for j in 1..9 loop
        dbms_output.put_line(i||':'||j);
        if j=5 then
          goto a; /*结束外层for循环,跳到标记位置*/
        end if;
      end loop;
    end loop;
  dbms_output.put_line('*');
  <<a>> /*在此位置定义一个标记*/
  dbms_output.put_line('*');
  dbms_output.put_line('***');
end;

练习

-- 练习1:从1开始累加 判断加到几时 和第一次大于1000
declare
    sumn int:=0; /*总数*/
    n int:=1; /*步长123456...*/
begin
    while sumn<=1000 loop 
       sumn:=sumn+n;
       n:=n+1;
       dbms_output.put_line(sumn||'--'||n);     
    end loop;
    dbms_output.put_line('加到'||n||'时,和第一次大于1000');  
    dbms_output.put_line(sumn);                                          
end;
-----------------------------------------------------------------------
-- 练习2:获取祖冲之计算到3.1415926到3.1415927时 计算的次数
-- 圆周率规律:4/1-4/3+4/5-4/7+4/9......
declare
    fm number:=1;
    pi number:=0.0;
    cs int:=0;
begin
    while pi > 3.1415927 or pi < 3.1415926 loop
        pi:=pi+4/fm;
        if fm<0 then
           fm:=fm-2;
        else
           fm:=fm+2;
        end if;
        fm:= -fm;/*只有正数才能参与运算*/
        cs:=cs+1;          
    end loop;
    dbms_output.put_line('当圆周率到 3.1415926 和 3.1415927 之间时,需要计算'||cs||'次!');
end;
----------------------------------------------------------------------
-- 练习3: 输入一个字符串 打印其大小写转换 并删除数字后的结果
declare
    str varchar(200):='&请输入一个字符串:';
    d varchar(200);
    c varchar(200);
begin
    for i in 1..length(str) loop
      d:=substr(str,i,1);
      if ascii(d)>ascii('A') and ascii(d)<ascii('Z') then
         d:=lower(d);
         c:=c||d;
      elsif ascii(d)>ascii('a') and ascii(d)<ascii('z') then
         d:=upper(d);
         c:=c||d;
      else ascii(d)>ascii('9') and ascii(d)<ascii('0') then
      	 c:=c||d;
      end if;

    end loop;
    dbms_output.put_line(c);
end;
-----------------------------------------------------------------------
-- 练习4:获取1到10000内 所有的完数:n的所有因子之和等于n :
--       如果m可以除尽n m就是n的因子 本身不是本身的因子 举例:6->1+2+3
-- 普通写法:
declare 
    wnum int;
begin
    for i in 1..10000 loop
        wnum:=0;
        for j in 1..i/2 loop
          if mod(i,j)=0 then
            wnum:=wnum+j;
          end if;
        end loop;
        if wnum=i and i!=1 then
           dbms_output.put_line(i);
        end if;  
    end loop;    
end;
-- 算法优化:
declare
  counts int:=0;
begin
  for i in 2..10000 loop  
    for j in 2..sqrt(i) loop
      if mod(i,j)=0 then
        counts:=counts+j+(i/j);
      end if;
      if mod(i,sqrt(i))=0 then
        counts:=counts+sqrt(i);
      end if;
    end loop;
      if i=counts+1 then
        dbms_output.put_line(i);
      end if;
      counts:=0;
  end loop;
end;

子程序

  子程序: 有名字的plsql块->编译和运行在数据库中
  子程序1: 存储过程   没有返回值的子程序  :procedure
  子程序2: 函数      有返回值的子程序    :function
  子程序的优点:
           >模块化
                一个子程序代表一个完整的功能
           >复用性
                定义完后 可以通过名字直接来调用
           >提高维护性
                直接定位方法 维护起来方便简单
           >提高安全性 
                调用者只能看到子查询到的声明 无法干涉程序具体执行
                可以通过设置权限 只让可信任的用户进行操作此程序
   存储过程的格式:
          create or replace procedure
                过程名字(参数列表)
          as|is
                定义变量
          begin
                实现功能的plsql代码
          exception
                处理异常的plsql代码
          end;   
-----------------------------------------------------------------------
 -- *** 调用存储过程 ***
 -- 使用plsql块调用
 begin
     pro_1(参数);
 end;  
 
 -- 使用execute--pl/sql developer软件不支持execute
 -- 打开oracle的客户端命令行:sql plus
 -- 开启客户端的输出流:set serverout on
 -- 执行execute命令
 execute pro_1(参数);
-----------------------------------------------------------------------
--1: 打印指定sid的学生的名字和分数信息
create or replace procedure
       pro_1(sidParam int)
as
       resultSname varchar(30);
       resultScore number(4,1);
begin
       select sname,sage into resultSname,resultScore
       from student
       where sid=sidParam;
       dbms_output.put_line(sidParam||'学生是:'||resultSname||',分数是:'||resultScore);
       exception
             when others then
             dbms_output.put_line('出现异常了!');  
end;
-----------------------------------------------------------------------
--2:特殊类型:%type->表里边什么类型,创建的属性就赋什么类型
create or replace procedure
       pro_2(sidParam int)
as
       resultSname student.sname%type;/*变量resultSname的数据类型和student.sname一致*/
       resultScore student.score%type;/*变量resultScore的数据类型和student.score一致*/
begin
       select sname,sage into resultSname,resultScore
       from student
       where sid=sidParam;
       dbms_output.put_line(sidParam||'学生是:'||resultSname||',分数是:'||resultScore);
       exception
             when others then
             dbms_output.put_line('出现异常了!');  
end;
-----------------------------------------------------------------------
--3:特殊类型:%rowtype
create or replace procedure
       pro_3(sidParam int)
as
       stu student%rowtype;/*stu变量的类型为student的行类型*/
begin
       select * into stu from student where sid=sidParam;
       dbms_output.put_line(sidParam||'学生是:'||stu.sname||',分数是:'||stu.score);
       exception
             when others then
             dbms_output.put_line('出现异常了!'); 
end;

练习

-- 练习1:从1开始累加 判断加到几时 和第一次大于1000
create or replace procedure
       pro_4
is
       n int:=1;
       sumn int:=0;
begin
       while sumn<1000 loop
             sumn:=sumn+n;
             n:=n+1;
       end loop;
       dbms_output.put_line('从1开始累加,加到'||(n-1)||'时,和第一次大于1000  sumn='||sumn); 
       exception
             when others then
             dbms_output.put_line('出现异常了!'); 
end;
-----------------------------------------------------------------------
-- 练习2:圆周率计算到3.1415926到3.1415927时,计算的次数
-- 规律: 4/1-4/3+4/5-4/7
create or replace procedure
       pro_5
is
       pi float:=0;
       npi float;
       ciShu int:=0;
begin
       while pi>3.1415927 or pi<3.1415926 loop
             ciShu:=ciShu+1;
             /*获取每次加入pi的值*/
             npi:=4/(ciShu*2-1);
             /*奇数加 偶数减 pi*/
             if mod(ciShu,2)=0 then
                pi:=pi-npi;
             else
                pi:=pi+npi;
             end if;
       end loop;
       dbms_output.put_line('圆周率计算到3.1415926到3.1415927时,计算的次数为:'||ciShu);
       exception
           when others then
           dbms_output.put_line('出现异常了!'); 
end;
-----------------------------------------------------------------------
-- 练习3: 输入一个字符串 打印其大小写转换 并删除数字后的结果
create or replace procedure
       pro_6
is
       str varchar(100):='ABCabc123!@#$';
       strNew varchar(100):='';
       lengthStr int;
       c varchar(3);
begin
       /*获取长度*/
       lengthStr:=length(str);
       for i in 1..lengthStr loop
           /*获取当前字符*/
           c:=substr(str,i,1);
           /*判断类型*/
           if c>='a' and c<='z' then
              strNew:=strNew||upper(c);
           elsif c>='A' and c<='Z' then
              strNew:=strNew||lower(c);
           elsif c>'9' or c<'0' then
              strNew:=strNew||c;
           end if;      
       end loop;
       dbms_output.put_line(str||'-->'||strNew);
       exception
           when others then
           dbms_output.put_line('出现异常了!'); 
end;
-----------------------------------------------------------------------
-- 练习4:获取1到10000内,所有的完数:n的所有因子之和等于n :
--        如果m可以除尽n m就是n的因子,本身不是本身的因子 举例: 6:1 2 3
create or replace procedure
       pro_7
is
       summ int;
begin
       for i in 1..10000 loop
           summ:=0;
           for j in 1..i/2 loop
               if mod(i,j)=0 then
                  summ:=summ+j;
               end if;
           end loop;
           if summ=i and i!=1 then
              dbms_output.put_line(i||'是完数!');
           end if; 
       end loop;
       exception
           when others then
           dbms_output.put_line('出现异常了!'); 
end;
-----------------------------------------------------------------------
=======================================================================
 -- 1 打印所有女生的基本信息:最高分 最低分 平均分 总人数
create or replace procedure
       pro_11(sexParam char)
is
       maxScore int;
       minScore int;
       avgScore int;
       countStu int;
begin
       select max(score),min(score),avg(score),count(*)
       into  maxScore,minScore,avgScore,countStu
       from student
       where sex=sexParam;
       dbms_output.put_line('最高分:'||maxScore||' 最低分:'||minScore||' 平均分:'||avgScore||' 总人数:'||countStu);
       exception
           when others then
           dbms_output.put_line('出现异常了!'); 
end;
-----------------------------------------------------------------------
create or replace procedure
       pro_12
is
       zhiShuSum int:=0;
       b boolean;
begin
       for i in 100..1000 loop
           b:=true;
           for j in 2..i-1 loop
               if mod(i,j)=0 then
                  b:=false;
               end if;
           end loop;
           if b and instr(i,'1',1)!=0 then
              dbms_output.put_line(i); 
              zhiShuSum:=zhiShuSum+i;
           end if;      
       end loop;
       dbms_output.put_line('质数的和为:'||zhiShuSum); 
       exception
           when others then
           dbms_output.put_line('出现异常了!');  
end;
-----------------------------------------------------------------------
-- 3 打印指定的二维乘法口诀
create or replace procedure
       pro_13
is
       hang int:=&请输入乘法表行列数:;
begin
       for i in 1..hang loop
           for j in 1..i loop
               dbms_output.put(j||'*'||i||'='||i*j);  
               if i*j<10 then
                  dbms_output.put('  ');  
               else
                  dbms_output.put(' '); 
               end if;
           end loop;
           dbms_output.put_line('');  
       end loop;
end;
-----------------------------------------------------------------------
-- 4 打印参数浮点类型,保留参数位数小数的四舍五入值
create or replace procedure
       pro_14(numb float,w int)
is
       newn float;
begin
       newn:=round(numb,w);
       dbms_output.put_line(newn);
end;
-----------------------------------------------------------------------
-- 5 判断参数int是不是质数
create or replace procedure
       pro_15(numb int)
is
       b boolean:=true;
begin
       for i in 2..numb-1 loop
           if mod(numb,i)=0 then
           b:=false;
           end if;
       end loop;
       if b then
          dbms_output.put_line(numb||':是质数!');
       else
          dbms_output.put_line(numb||':不是质数!');
       end if;
end;
-----------------------------------------------------------------------
-- 6 求参数字符串表示的时间距离当前时间有多少天
declare
   myYear int:='&请输入年';
   myMonth integer:='&请输入月';
   myDay int:='&请输入日';
begin
   pro_16(myYear,myMonth,myDay);
end;
-----------------------------------
create or replace procedure
       pro_16(myYear int,myMonth int,myDay int)
is
       days int;
       myDate date;
begin
       myDate:=to_date(myYear||'-'||myMonth||'-'||myDay,'yyyy-mm-dd');
       days:=abs(myDate-sysdate);
       dbms_output.put_line(myYear||'-'||myMonth||'-'||myDay||':距离今天的天数是:'||days);
       exception
       	  when others then
          dbms_output.put_line('出错了!');
end;
----------------------------------------------------------------------
-- 7 获取参数字符串中所有数字组成的最大整数
create or replace procedure
       pro_17(str varchar)
as
       shuZi varchar(100);
       maxSZ varchar(100);
begin
       for i in 1..length(str) loop
           if substr(str,i,1)>='0' and substr(str,i,1)<='9' then
              shuZi:=shuZi||substr(str,i,1);  
           end if;
       end loop;
       dbms_output.put_line(shuZi);
       for i in 0..9 loop
           for j in 1..length(shuZi) loop
              if substr(shuZi,j,1)=i then
                 maxSZ:=substr(shuZi,j,1)||maxSZ;
              end if;
           end loop;
       end loop;
       dbms_output.put_line(maxSZ);
end;
=======================================================================
-- 1 打印所有女生的基本信息: 最高分  最低分 平均分  总人数
--   参数不能加精度   
create or replace procedure
     pro_1(sexParam char)
as
     maxScore float;
     minScore float;
     avgScore float;
     countRenShu int;
begin
   select max(nvl(score,0)),min(nvl(score,0)),avg(nvl(score,0)),count(*)
        into maxScore,minScore,avgScore,countRenShu
   from student
   where sex=sexParam; 
   dbms_output.put_line(sexParam||'生的统计信息是:最高分='||maxScore||',最低分='||minScore||',平均分='||avgScore||',总人数='||countRenShu);        
   exception
        when others then
        dbms_output.put_line('出错了!'); 
end; 
     
begin
 pro_1('女');
 dbms_output.put_line('---------------'); 
 pro_1('男');
end;
-----------------------------------------------------------------------
-- 2 打印100到1000内所有位数上含有1的质数的和
create or replace procedure
       pro_2
as
       sumn int:=0;
       b boolean;
       strn varchar(4);
begin
       for n in 100..1000 loop
           /*判断当前n是不是含有1*/
           strn:=n||'';
           /*字符串索引从1开始*/ 
           if instr(strn,'1',1)!=0 then
              /*判断当前数字是不是质数*/
              b:=true;
              for m in 2..n-1 loop
                  if mod(n,m)=0 then
                     b:=false;
                     exit;
                  end if;
              end loop;
              if b and n!=1 then
                 dbms_output.put_line(n||'是数上含有1的质数'); 
                 sumn:=sumn+n;
              end if;
           end if;
       end loop;
       dbms_output.put_line('100到1000内所有位数上含有1的质数的和='||sumn); 
       exception
           when others then
           dbms_output.put_line('出错了!');
end;

begin
 pro_2();
end;
-----------------------------------------------------------------------
-- 3 打印指定的二维乘法口诀:
create or replace procedure
       pro_3(n int)
as

begin
       if n=-1 then
          for i in 1..9 loop
              for j in 1..i loop
                  dbms_output.put(j||'*'||i||'='||i*j);
                  if i*j>=10 then
                     dbms_output.put(' ');
                  else
                     dbms_output.put('  ');
                  end if;
              end loop;
              dbms_output.put_line('');
          end loop;
       elsif n=0 then
             for i in 1..9 loop
                 for j in 1..9 loop
                     if (10-j)>=i then
                        dbms_output.put(j||'*'||(10-i)||'='||(10-i)*j);
                     end if;
                     if (10-i)*j>=10 then
                        dbms_output.put(' ');
                     else
                        dbms_output.put('  ');
                     end if;
                 end loop;
                 dbms_output.put_line('');
             end loop; 
       elsif n=1 then
             for i in 1..9 loop
                 for j in 1..9 loop
                    if (10-i)>=(10-j) then
                        dbms_output.put((10-j)||'*'||(10-i)||'='||(10-i)*(10-j));
                        if (10-i)*(10-j)>=10 then
                           dbms_output.put(' ');
                        else
                           dbms_output.put('  ');
                        end if;
                    else
                        dbms_output.put('       '); 
                    end if;
                 end loop;
                 dbms_output.put_line('');
             end loop;
       else
             for i in 1..9 loop
                 for j in 1..9 loop
                     if i>=(10-j) then  
                         dbms_output.put((10-j)||'*'||i||'='||i*(10-j));
                         if i*(10-j)>=10 then
                            dbms_output.put(' ');
                         else
                            dbms_output.put('  ');
                         end if;
                     else 
                        dbms_output.put('       '); 
                     end if;
                 end loop;
                 dbms_output.put_line('');
             end loop;
       end if;
end;

begin
  pro_3(-1);
  dbms_output.put_line('--------------------------------------------------');
  pro_3(0);
  dbms_output.put_line('--------------------------------------------------');
  pro_3(1);
  dbms_output.put_line('--------------------------------------------------');
  pro_3(2);
end;
-----------------------------------------------------------------------
-- 4 打印参数浮点类型 保留参数位数小数的四舍五入值
--   123.456789   3  ----123.456
create or replace procedure
       pro_4(n number,weiShu int) /* 123.456789,3*/
as
       n1 float;
       n2 float;
       n3 int;
       n4 float;
       n10 int:=power(10,weiShu);
begin
       dbms_output.put_line('解决方法1:'||round(n,weiShu)); 
       n1:=n*n10; /*123456.789*/
       n3:=trunc(n1); /*123456*/
       n2:=n1-n3;/*0.789*/
       if n2>0.5 then
          n3:=n3+1;
       end if;
       n4:=n3/n10;
       dbms_output.put_line(n||'四舍五入取'||weiShu||'位='||n4); 
       exception
           when others then
           dbms_output.put_line('出错了!'); 
end;

begin
 pro_4(123.456789,3);       
end;
-----------------------------------------------------------------------
-- 5 判断参数int是不是质数
create or replace procedure
       pro_5(n number)
as
       b boolean:=true;
begin
       for i in 2..n-1 loop
           if mod(n,i)=0 then
              b:=false;
              exit;
           end if;
       end loop;
       if b and n>1 then
          dbms_output.put_line(n||'是质数'); 
       else
          dbms_output.put_line(n||'不是质数'); 
       end if;
       exception
           when others then
           dbms_output.put_line('出错了!'); 
end;

begin 
 pro_5(1);
end;
-----------------------------------------------------------------------
-- 6 求参数字符串表示的时间距离当前时间有多少天
create or replace procedure
      pro_6(str varchar) 
as
      days int;
      dateStr date;
begin
      dateStr:=to_date(str,'yyyy-mm-dd hh24:mi:ss');
      days:=abs(dateStr-sysdate);
      dbms_output.put_line(str||' 距离当前时间的天数是:'||days);
      exception
         when others then
         dbms_output.put_line('出错了!');                        
end;
begin
 pro_6('2022-1-30'); 
end;
-----------------------------------------------------------------------
-- 7 获取参数字符串中所有数字组成的最大整数
create or replace procedure
       pro_7(str varchar)
as
       c varchar(3); 
       nStr varchar(4000):='';  
       cmax varchar(3);
       indexMax int;
       maxStr varchar(4000):='';
begin
       /*获取str中所有的数字字符*/
        for i in 1..length(str) loop
            c:=substr(str,i,1);
            if c>='0' and c<='9' then
               nStr:=nStr||c;
            end if;
        end loop;
        dbms_output.put_line(str||'---'||nStr); 
        /*对所有的数字字符进行排序*/
        for i in 1..length(nStr) loop
            /*获取nStr中最大的字符*/
            cmax:=substr(nStr,1,1);
            indexMax:=1;
            for j in 1..length(nStr) loop
                c:=substr(nStr,j,1);
                if c>cmax then
                   cmax:=c;
                   indexMax:=j;
                end if;
            end loop;
            /*把最大的字符加入maxStr中*/
            maxStr:=maxStr||cmax;
            /*把nStr中indexMax处的字符删除123 5 46*/
            nStr:=substr(nStr,1,indexMax-1)||subStr(nStr,indexMax+1);
        end loop;
            dbms_output.put_line(str||'其中最大的整数是:'||maxStr);
        exception
            when others then
            dbms_output.put_line('出错了!'); 
end;
begin
 pro_7('@#!asd1773466546!@#!');
end;

存储过程 参数模式

    参数列表:定义方法时 定义的变量列表 用于接收方法运行所需要的原始数据
    返回值:方法调用后 返回给调用者的结果数据
    参数列表:接收原始数据的变量
    返回值:调用后的结果数据

    存储过程:procedure:子程序:没有返回值的子程序
    in模式的参数:接受原始数据的参数(默认)
                 在代码中不能给in模式的参数赋值
    out模式的参数:用于返回结果数据的参数
    in out模式的参数:既能接收原始数据 还能返回结果数据
-----------------------------------------------------------------------
-- 给用户开启一个空间
create tablespace wen datafile 'E:\oracle\WEN.DBF' size 10M;
-- 给用户设置密码,并把用户放到空间里
create user WEN identified  by "123" default tablespace WEN;
-- 给用户赋予登录权限
grant create session to WEN;
-- 授予指定用户存储过程的使用权限
grant execute on WEN.pro_1 to WEN;
-- 撤销指定用户存储过程的使用权限
revoke execute on WEN.pro_1 from WEN;
-- 删除存储过程
drop procedure pro_1;  
-----------------------------------------------------------------------
-- 打印两个int的和
create or replace procedure
       pro_1(n1 in int,n2 in int) /*in模式的参数*/
as
       sumn int;
begin
       /* n1:=1+1; in模式参数 不能再次赋值 */
       sumn:=n1+n2;
       dbms_output.put_line(n1||'+'||n2||'='||sumn); 
end;
-----------------------------------------------------------------------
-- 获取两个int的和
create or replace procedure
       pro_2(n1 in int,n2 in int,sumn out int) /*sumn用于获取结果数据*/
as
  
begin
       sumn:=n1+n2;
       dbms_output.put_line(n1||'+'||n2||'='||sumn); 
end;

declare
       sum1 int:=1;/*此时的初始值没有意义*/
begin
       pro_2(1,3,sum1);/*sum1参数用于接受存储过程中out模式的参数sumn*/
       dbms_output.put_line('sum1='||sum1);
end;
-----------------------------------------------------------------------
-- 获取一个int数的二次方
create or replace procedure
       pro_3(n in out int) /*in模式接收原始数据  out模式用于返回结果数据*/
as     
begin
       n:=power(n,2);
       dbms_output.put_line(n||'的2次方='||n);
end;

declare
       m int:=3;
begin
       pro_3(m);
end;
-----------------------------------------------------------------------
-- 获取ab的和 并把a+1 
create or replace procedure
       pro_4(a in out int,b int,sumab out int)
as
begin
       sumab:=a+b;
       a:=a+1; 
end;

declare
       sum1 int;
       aa int:=4;
begin
       pro_4(aa,3,sum1); 
       dbms_output.put_line('aa='||aa||',和='||sum1);
end;
-----------------------------------------------------------------------
-- 获取a的二次方 三次方 四次方 开方 并+1
create or replace procedure
       pro_5(a1 in out int,a2 out int,a3 out int,a4 out int,akf out number)
as
begin
       a2:=power(a1,2);
       a3:=power(a1,3);
       a4:=power(a1,4);
       akf:=power(a1,0.5);
       a1:=a1+1;
end;

declare
       aa int:=10;
       aa2 int;
       aa3 int;
       aa4 int;
       aakf number;
begin
       pro_5(aa,aa2,aa3,aa4,aakf);
       dbms_output.put_line('aa='||aa||',aa2='||aa2||',aa3='||aa3||',aa4='||aa4||',aakf='||aakf);
end;

异常

异常:程序运行出现非正常清空
     在plsql块中,正常代码出现异常 将终止执行正常代码 然后跳转到exception对应的代码
     
oracle中常见的异常:
    ACCESS_INTO_NULL 
    未定义对象 

    CASE_NOT_FOUND 
    CASE 中若未包含相应的 WHEN ,并且没有设置 ELSE 时 

    COLLECTION_IS_NULL 
    集合元素未初始化 

    CURSER_ALREADY_OPEN 
    游标已经打开 

    DUP_VAL_ON_INDEX 
    唯一索引对应的列上有重复的值 

    INVALID_CURSOR 
    在不合法的游标上进行操作 

    INVALID_NUMBER 
    内嵌的 SQL 语句不能将字符转换为数字 

    NO_DATA_FOUND 
    使用 select into 未返回行,或应用索引表未初始化的元素时 

    TOO_MANY_ROWS 
    执行 select into 时,结果集超过一行 

    ZERO_DIVIDE 
    除数为 0 

    SUBSCRIPT_BEYOND_COUNT 
    元素下标超过嵌套表或 VARRAY 的最大值 

    SUBSCRIPT_OUTSIDE_LIMIT 
    使用嵌套表或 VARRAY 时,将下标指定为负数 

    VALUE_ERROR 
    赋值时,变量长度不足以容纳实际数据 

    LOGIN_DENIED 
    PL/SQL 应用程序连接到 oracle 数据库时,提供了不正确的用户名或密码 

    NOT_LOGGED_ON 
    PL/SQL 应用程序在没有连接 oralce 数据库的情况下访问数据 

    PROGRAM_ERROR 
    PL/SQL 内部问题,可能需要重装数据字典& pl./SQL 系统包 

    ROWTYPE_MISMATCH 
    宿主游标变量与 PL/SQL 游标变量的返回类型不兼容 

    SELF_IS_NULL 
    使用对象类型时,在 null 对象上调用对象方法 

    STORAGE_ERROR 
    运行 PL/SQL 时,超出内存空间 

    SYS_INVALID_ID 
    无效的 ROWID 字符串 

    TIMEOUT_ON_RESOURCE 
    Oracle 在等待资源时超时  
-----------------------------------------------------------------------
declare
     a1 int;
begin
     insert into student(sid,sname) values(1,'红尘');
     a1:=1;
     case a1
          when 2 then
               dbms_output.put_line('a1=2');
          when 3 then
               dbms_output.put_line('a2=3');   
     end case; 
     select sage into a1 from student;
     select sage into a1 from student where sid=6;
     select to_number('abc1') into a1 from dual;
     a1:=2/0; 
     exception
     when DUP_VAL_ON_INDEX then
          dbms_output.put_line('出现异常:DUP_VAL_ON_INDEX:违反了唯一约束');
     when SELF_IS_NULL then
          dbms_output.put_line('出现异常:SELF_IS_NULL:调用null的方法');
     when CASE_NOT_FOUND then
          dbms_output.put_line('出现异常:CASE_NOT_FOUND:没有找到对应的case 并且没有else');
     when TOO_MANY_ROWS then
          dbms_output.put_line('出现异常:TOO_MANY_ROWS:数据超出一行');
     when NO_DATA_FOUND then
          dbms_output.put_line('出现异常:NO_DATA_FOUND:没有获取数据');
     when INVALID_NUMBER then
          dbms_output.put_line('出现异常:INVALID_NUMBER:数字转换异常');
     when ZERO_DIVIDE then
          dbms_output.put_line('出现异常:ZERO_DIVIDE:被0除!');
     when others then
          dbms_output.put_line('出现异常:出现其他异常!');
end;

自定义异常

declare
          age int:='&请输入年龄';
          MY_AGE_EXCEPTION exception; /*自定义异常*/
begin
          /*指定出现自定义异常的条件*/
          if age<=0 or age>120 then
             raise MY_AGE_EXCEPTION; /*产生异常情况*/
          end if;
          dbms_output.put_line('age='||age);
          exception
          when MY_AGE_EXCEPTION then
               dbms_output.put_line('出现异常:MY_AGE_EXCEPTION:年龄范围出错!');
               /*通过弹出框 显示异常编号和异常原因*/
               RAISE_APPLICATION_ERROR(-20001,'年龄范围出错:必须是[0,120]');
          when others then
               dbms_output.put_line('出现异常:出现其他异常!');
end;

函数

函数;function
     有返回值的子程序:对功能的封装
格式: 
    create or replace function
         函数名字(参数列表)  
    return 返回值类型
    as|is
         定义变量
    begin
         实现功能的代码块
    esception
         处理异常的代码块
    end; 
-----------------------------------------------------------------------
注意:
	如果function的参数类型或者返回值值类型是sql支持的类型或者%type  
    这样的function可以被plsql调用也可以被select调用 
    
   	如果function的参数类型或者返回值值类型是boolean或者%rowtype  
    这样的function只能被plsql调用,不能被select调用 
-----------------------------------------------------------------------
-- 求一个int和一个double的和
create or replace function
       f_1(a int,b float)
return float
as
       sumab float;
begin 
       sumab:=a+b;
       return sumab;
exception
       when others then
       dbms_output.put_line('出现异常了!');       
end;

-- 调用函数:方式1:plsql块
declare
   sumab float;
begin
   sumab:=f_1(3,11.1);
   dbms_output.put_line('sumab='||sumab);  
end;
-- 调用函数:方式2:select
select f_1(2,11.1) from dual;
-----------------------------------------------------------------------
-- 无参数的函数
-- 获取当前时间对应的字符串:格式是xxxx年xx月xx日
create or replace function
       f_2 /*和存储过程相同  如果没有参数列表()不能写*/
return varchar
as
       str varchar(100);
begin
       str:=to_char(sysdate,'yyyy"年"mm"月"dd"日"');
       return str;
exception
       when others then
       dbms_output.put_line('出现异常了!');       
end;

-- 调用函数:方式2:select
select f_2() from dual;
-----------------------------------------------------------------------
--  %type类型 可以被select调用
create or replace function
     f_3(sidMy student.sid%type)     
return student.sage%type
as
   sageMy student.sage%type;
begin
     select sage into sageMy from student where sid=sidMy;
     return sageMy;
exception
     when others then
     dbms_output.put_line('出现异常了!');      
end;
-- 调用函数:方式2:select 
select f_3(2) FROM dual;
-----------------------------------------------------------------------
-- 判断boolean 可以被select调用
-- 如果b为true 获取a+1的值 如果b为false获取a-1的值
-- 参数是boolean
create or replace function
       f_4(a int,b boolean)
return int
as
       aa int;
begin
       if b then
          aa:=a+1;
       else
          aa:=a-1;
       end if;
       return aa;
exception
       when others then
       dbms_output.put_line('出现异常了!');      
end;

-- 调用函数:plsql调用
declare
    aa int;
begin
    aa:=f_4(10,true);
    dbms_output.put_line('aa='||aa);
end;
-- 调用函数:select
-- 参数是boolean的function 不能被select调用
select f_4(11,true) from dual;
----------------------------------------------------------------------
-- 返回值是boolean
create or replace function
     f_5(a int )     
return boolean
as
begin
     return a>0;
exception
     when others then
     dbms_output.put_line('出现异常了!');      
end;

-- 调用:通过plsql调用
declare
    bb boolean;
begin
    bb:=f_5(1);
    if bb then
        dbms_output.put_line('bb=true');
    else
        dbms_output.put_line('bb=false');
    end if;
end;
-- 调用函数:select
--返回值是boolean的function 不能被select调用
select f_5(1) from dual;
-----------------------------------------------------------------------
-- 判断rowtype类型的参数是否可以被select调用
create or replace function
     f_6(stu student%rowtype)     
return int
as
    sageMy int;
begin
    sageMy:=stu.sage;
    return sageMy;
exception
     when others then
     dbms_output.put_line('出现异常了!');      
end;
-- 调用:通过plsql调用
declare
    sageMy int;
    stu student%rowtype;
begin
   select * into stu from student where sid=2;
   sageMy:=f_6(stu);
   dbms_output.put_line('sageMy='||sageMy);  
end;
-- 调用:通过select
declare
    stu student%rowtype;
begin
   select * into stu from student where sid=2;
   --参数是%rowtype的function 不能被select调用
   select f_6(stu) from dual;  
end;
-----------------------------------------------------------------------
-- 判断rowtype类型的返回值是是否可以被select调用
create or replace function
     f_7(sidMy int)     
return student%rowtype
as
     stu student%rowtype; 
begin
     select * into stu from student where sid=sidMy;
     return stu;     
end;
-- 调用:通过plsql调用
declare
   stu student%rowtype;
begin
   stu:=f_7(2);
   dbms_output.put_line(stu.sid||','||stu.sname);  
end;
-- 调用:通过select
select f_7(2) from dual;

练习

--1 判断一个数是不是质数
create or replace function
  fun_1(n int)
return boolean
as
begin
   for m in 2..sqrt(n) loop
        if mod(n,m)=0  then
            return false;
        end if;
   end loop;
   return n>1;
end;
-- 调用
begin
   for n in 1..100 loop
        if fun_1(n) then 
           dbms_output.put_line(n||'是质数!');
        end if;
   end loop;
end;
-----------------------------------------------------------------------
--2 获取祖冲之老先生计算到3.1415926到3.1415927之间用了多少次
create or replace function
  fun_2
return int
as
   ciShu int:=0;
   pi float:=0;
   piNum float;
begin
   while pi>3.1415927 or pi <3.1415926 loop
          ciShu:=ciShu+1;
          piNum:=4/(ciShu*2-1);
          if mod(ciShu,2)=0 then
               piNum:=piNum*(-1);
          end if;
          pi:=pi+piNum;
   end loop;
   return ciShu;
end;
-- 调用
begin
   dbms_output.put_line('用的次数是:'||fun_2());
end;
-----------------------------------------------------------------------
--3 数学黑洞:一个四位数 所有位数不是完全重复 进行如下运算
--  运算法则:当前数是n:取各个位数的值 组成一个最大值 再组成一个最小值 相减 循环操作
--          最多7次 变为6174 7641-1467=6174
create or replace function
   fun_3(n int)
return int
as
   ciShu int:=0;
   a int;b int;c int;d int; k int;
   maxn int;minn int; 
   m int:=n;  
begin
   while m!=6174 loop
       /*获取个位数的值*/
       a:=mod(trunc(m/1),10);
       b:=mod(trunc(m/10),10);
       c:=mod(trunc(m/100),10);
       d:=mod(trunc(m/1000),10);

       for i in 1..3 loop
           if a < b then
               k:=a; a:=b; b:=k; 
           end if;
           if b < c then
               k:=b; b:=c; c:=k; 
           end if;
           if c < d then
               k:=c; c:=d; d:=k; 
           end if;
       end loop;
       /*获取最大值和最小值*/
       maxn:=1000*a+100*b+10*c+d;
       minn:=1000*d+100*c+10*b+a;
       m:=maxn-minn;
       ciShu:=ciShu+1;
   end loop;
   return ciShu;
end;
-- 调用

begin
   for n in 1..10000 loop
        if n>=1000 and n<10000 and mod(n,1111)!=0 then 
           dbms_output.put_line(n||':'||fun_3(n));
        end if;
   end loop;
end;
-----------------------------------------------------------------------
-- 4 获取参数年和参数月 有多少天
create or replace function
  fun_4(myYear int,myMonth int)
return int
as
begin
   case myMonth
   when 4 then
             return 30;
   when 6 then
             return 30;
   when 9 then
             return 30;
   when 11 then
             return 30;
   when 2 then
            if (mod(myYear,4)=0 and mod(myYear,100)!=0) or (mod(myYear,400)=0) then
                  return 29;
            end if;
            return 28;
   else
            return 31;
   end case;         
end;
-- 调用
begin
   for myYear in 2000..2020 loop
        for myMonth in 1..12 loop
              dbms_output.put_line(myYear||'年'||myMonth||'月'||'天数是:'||fun_4(myYear,myMonth));
        end loop;
   end loop;
end;
-----------------------------------------------------------------------
-- 5 写一个函数fun_5(n) 打印参数行的空心菱形  n为大于等于5的奇数 n为总行数
/*
           行数:     起始位置           结束位置
   *        1        (n+1)/2          (n+1)/2    
  ***       2        (n+1)/2-1        (n+1)/2+1
 *****      3        (n+1)/2-2        (n+1)/2+2
*******     4        (n+1)/2-3        (n+1)/2+3

            i        (n+1)/2+1-i      n+1)/2-1+i 
            
 *****      1           2                n-1
  ***       2       	3                n-2
   *
            i          i+1               n-i
*/
create or replace procedure
  pro_5(n int)
as
begin
   /*前(n+1)/2行*/
   for i in 1..(n+1)/2 loop
        for j in 1..n loop
             if j >= ((n+1)/2+1-i) and j <=((n+1)/2-1+i) then
                  dbms_output.put('*');
             else
                  dbms_output.put(' ');
             end if;
        end loop;
        dbms_output.put_line('');
   end loop;
    /*后(n+1)/2-1行*/
   for i in 1..(n+1)/2-1 loop
        for j in 1..n loop
             if j >= i+1 and j <=n-i then
                  dbms_output.put('*');
             else
                  dbms_output.put(' ');
             end if;
        end loop;
        dbms_output.put_line('');
   end loop;
end;
-- 调用
begin
   for n in 3 ..20 loop
        if mod(n,2)!=0 then
             pro_5(n);
        end if;
   end loop;
end;

--- 空心菱形
create or replace procedure
  pro_5(n int)
as
begin
   /*前(n+1)/2行*/
   for i in 1..(n+1)/2 loop
        for j in 1..n loop
             if j = ((n+1)/2+1-i) or j =((n+1)/2-1+i) then
                  dbms_output.put('*');
             else
                  dbms_output.put(' ');
             end if;
        end loop;
        dbms_output.put_line('');
   end loop;
    /*后(n+1)/2-1行*/
   for i in 1..(n+1)/2-1 loop
        for j in 1..n loop
             if j = i+1 or j =n-i then
                  dbms_output.put('*');
             else
                  dbms_output.put(' ');
             end if;
        end loop;
        dbms_output.put_line('');
   end loop;
end;
-- 调用
begin
   for n in 3 ..20 loop
        if mod(n,2)!=0 then
             pro_5(n);
        end if;
   end loop;
end;

游标

游标: cursor
      类似于集合中的迭代器  用于遍历结果集
分类: 隐式游标:进行dml语句时 数据库自动创建的游标
      显式游标:进行dql语句时 由程序员自己创建的游标
属性: %FOUND:    判断是否还有元素可以遍历
      %NOTFOUND: 判断是否已无元素可以遍历
      %ROWCOUNT: 获取影响的行数
      %ISOPEN:   判断游标是否开启      
隐式游标  默认统一名字SQL  
         默认是关闭的:%ISOPEN= false  
         
/*
  定义游标
       cursor 游标名字 as select xxx   
  使用游标:
       open  c_1; 开启游标
       fetch c_1  into 遍历 
       close c_1; 关闭游标
*/
-----------------------------------------------------------------------
-- 获取多行一列
declare
   cursor c_1 is select score from student;/*定义游标*/
   myScore float;
begin
   open c_1;/*开启游标*/
   loop
        fetch c_1 into myScore;
        if c_1%NOTFOUND then
           exit;/*结束循环*/     
        end if;
        dbms_output.put_line('myScore='||myScore);
   end loop;
   close c_1;/*关闭游标*/
end;
-----------------------------------------------------------------------
-- 获取多行多列
declare 
   cursor c_2 is select * from student;/*定义游标*/
   stu student%rowtype;
begin
   open c_2;/*开启游标*/
   loop
        fetch c_2 into stu;
        if c_2%NOTFOUND then
           exit;  /*结束循环*/
        end if;
        dbms_output.put_line(stu.sid||'-'||stu.sname||'-'||stu.sage); 
   end loop;
   close c_2;/*关闭游标*/ 
end;
----------------------------------------------------------------------
-- 获取多行多列
declare
   cursor c_3 is select sid,sname,sage from student;
   mysid student.sid%type;
   mysage student.sage%type;
   mysname student.sname%type;
begin
   open c_3;
   loop
        fetch c_3 into mysid,mysname,mysage;
        if c_3%NOTFOUND then
           exit;
        end if;
        dbms_output.put_line(mysid||'-'||mysage||'-'||mysname);
   end loop;
   close c_3;
end;
-----------------------------------------------------------------------
-- 快捷写法1 获取多行多列
declare
   cursor c_4 is select sid,sname,sage from student;
   mysid student.sid%type;
   mysname student.sname%type;
   mysage student.sage%type;
begin
   open c_4;
   loop
        fetch c_4 into mysid,mysname,mysage;
        exit when c_4%notfound;
        dbms_output.put_line(mysid||'-'||NVL(mysage,0)||'-'||mysname); 
   end loop;
   close c_4; 
end;
-----------------------------------------------------------------------
--快捷写法2 使用for遍历游标
/*
  for 变量 in 游标 loop
      操作变量的代码
  end loop;
*/
-----------------------------------------------------------------------
-- 案例1:获取所有列
declare
   cursor c_5 is select * from student;
begin
   for stu in c_5 loop
       dbms_output.put_line(stu.sid||'-'||stu.sname||'-'||stu.sage); 
   end loop;
end;
-- 案例2:获取部分列
declare
   cursor c_6 is select sid,sname,sage from student;
begin
   for stu in c_6 loop
       dbms_output.put_line(stu.sid||'-'||stu.sname||'-'||stu.sage); 
   end loop;
end;

游标遍历

/*
  游标:类似于集合的迭代器  用于遍历结果集
      :cursor
  格式:cursor 游标名 is select 语句    
*/
-----------------------------------------------------------------------
-- 遍历游标1
declare
  cursor c_1 is select * from student;
  stu student%rowtype;
begin
  open c_1;
       loop
         fetch c_1 into stu;
         if c_1%notfound then   
            exit;
         end if;
         dbms_output.put_line(stu.sid||','||stu.sname||','||nvl(stu.score,0));
       end loop;
  close c_1;
end;
-----------------------------------------------------------------------
-- 遍历游标2
declare
   cursor c_2 is select * from student;
begin
   for stu in c_2 loop
       dbms_output.put_line(stu.sid||','||stu.sname||','||nvl(stu.score,0));
   end loop;
end;

游标对记录dml

/*
  通过游标对记录进行dml
  格式:cursor  游标名  is  select语句  for update
*/
-----------------------------------------------------------------------
declare
  cursor c_3 is select * from student for update;
begin
  for stu in c_3 loop
      dbms_output.put_line(stu.sid||','||stu.sname||','||nvl(stu.score,0));
      if stu.sex='女' then
        update student set score=score+1 where current of c_3;/*更改游标的当前行记录*/
      else
        delete student where current of c_3;/*删除游标的当前行记录*/ 
      end if;
  end loop;
  commit;/*提交事务*/ 
  dbms_output.put_line('修改成功!');
  exception 
      when others then
      rollback;/*回滚*/
      dbms_output.put_line('修改失败!');
end;

结构

/*
包:package:对数据库对象进行分类管理
    数据库对象:user table sequence index view cursor function procedure等
包=包声明+包体
包声明格式
    create or replace package
        包名
    as|is
       function 函数名(参数列表) return 返回值类型;
       function 函数名(参数列表) return 返回值类型;
       procedure 过程名(参数列表);   
       procedure 过程名(参数列表);
    end 包名;   
    
包体格式
    create or replace package body
        包名
    as|is
       function 函数名(参数列表) return 返回值类型 
       as
            定义变量
       begin
            实现功能的代码块
            return 返回值;
       end 函数名;
       
       function 函数名(参数列表) return 返回值类型
       as
            定义变量
       begin
            实现功能的代码块
            return 返回值;
       end 函数名;
       
       procedure 过程名(参数列表)
       as
            定义变量
       begin
            实现功能的代码块
       end 过程名;  
       procedure 过程名(参数列表);
       
       procedure 过程名(参数列表)
       as
            定义变量
       begin
            实现功能的代码块
       end 过程名;  
       procedure 过程名(参数列表);
    end 包名;   
*/

使用

-- 包声明
create or replace package
       package_1
is
       function f_1(n int) return boolean;/*判断是不是质数*/
       function f_2(str varchar) return varchar;/*对字符串进行转换*/
       procedure p_1(n in out int,m int);/*用n获取n+m*/
       procedure p_2(str in out varchar);/*用s1获取s1所有不重复的字符*/
      
end    package_1;
-----------------------------------------------------------------------
-- 包体
create or replace package body
       package_1
is
       function f_1(n int) return boolean
       as
       begin
         	for m in 2..n-1 loop
              if mod(n,m)=0 then
                 return false;
              end if;
          end loop;
              return n>1;
       end f_1;
       
       function f_2(str varchar) return varchar
       as
          strNew varchar(4000):='';
          c varchar(3);
       begin
          for i in 1..length(str) loop
              c:=substr(str,i,1);
              if c>='a' and c<='z' then
                 c:=upper(c);
                 strNew:=strNew||c;
              elsif c>='A' and c<='Z' then
                 c:=lower(c);
                 strNew:=strNew||c;
              elsif c>'9'or c<'0'then
                 strNew:=strNew||c;
              end if;
          end loop;
          return strNew;
       end f_2;
       
       procedure p_1(n in out int,m int)
       as
       begin
           n:=n+m;
       end p_1;
       
       procedure p_2(str in out varchar)
       as
           strNew varchar(4000):=substr(str,1,1);
           c varchar(3);
       begin
           for i in 2..length(str) loop
               c:=substr(str,i,1);
               dbms_output.put_line(c||'-'||instr(strNew,c));
               if instr(strNew,c,1)=0 then
                  strNew:=strNew||c;
               end if;       
           end loop;
           str:=strNew;
       end p_2;
end package_1;
-----------------------------------------------------------------------
-- 调用
begin
  for n in 1..100 loop
      if package_1.f_1(n) then
         dbms_output.put_line(n||'是质数');
      end if;
  end loop;
end;

select package_1.f_2('asdASD!@#$') from dual;

declare
       a int:=100;
begin    
       package_1.p_1(a,200);
       dbms_output.put_line(a);
end;

declare
    str varchar(20):='&请输入一个字符串:';
begin
    package_1.p_2(str);
    dbms_output.put_line('转换后:'||str);    
end;

触发器

概念

/*
触发器:trigger:可以自动执行的存储过程
        当指定事件发生时  自动执行的plsql块
注意:触发器不能被主动调用
作用:
    --自动生成数据
    --自定义复杂的安全权限
    --提供审计和日志记录
    --启用复杂的业务逻辑
分类:根据事件不同分类
    -- ddl(模式)触发器:对表进行创建 修改表结构 删除表
    -- dml触发器:执行dml语句:delete update insert
         --- 行级触发器:   dml语句执行时影响几行 触发器就执行多少次
         --- 语句级触发器: dml语句执行一次  触发器只执行一次
    -- 数据库级触发器: (略)
        登录 退出 创建连接 关闭连接等事件时执行的触发器 
*/

格式

注意:dml触发器 有三个boolean属性updating,deleting,inserting 获取当前dml语句的类型
格式1:
    --dml语句级触发器
      create or replace trigger
          触发器名
      after|before  insert[or delete or update] on 表名
      [declare 定义变量 ]
      begin
          plsql块
      end;    
      
格式2:
    --dml行级触发器
      create or replace trigger
          触发器名
      after|before  insert[or delete or update] on 表名
      for each row
      [declare 定义变量 ]
      begin
          plsql块
      end;  
注意:行级触发器中有两个变量:old和:new 分别记录的是触发器执行前后的行的信息       

/* 
    -- 启用触发器
           alter trigger  tri_1 enable ;
    -- 禁用触发器
           alter trigger  tri_1 disable ;  
    -- 删除触发器
           drop trigger  tri_1;
    -- 语句级触发器
*/

属性

/*
ddl触发器可以使用的属性:
ORA_CLIENT_IP_ADDRESS	客户端IP地址
ORA_DATABASE_NAME	数据库名称
ORA_DES_ENCRYPTED_PASSWORD	当前用户的DES算法加密后的密码
ORA_DICT_OBJ_NAME	触发DDL的数据库对象名称
ORA_DICT_OBJ_NAME_LIST	受影响的对象数量和名称列表
ORA_DICT_OBJ_OWNER	触发DDL的数据库对象属主
ORA_DICT_OBJ_OWNER_LIST	受影响的对象数量和名称列表
ORA_DICT_OBJ_TYPE	触发DDL的数据库对象类型
ORA_GRANTEE	被授权人数量
ORA_INSTANCE_NUM	数据库实例数量
ORA_IS_ALTER_COLUMN	如果操作的参数column_name指定的列,返回true,否则false
ORA_IS_CREATING_NESTED_TABLE	如果正在创建一个嵌套表则返回true,否则false
ORA_IS_DROP_COLUMN	如果删除的参数column_name指定的列,返回true,否则false
ORA_LOGIN_USER	触发器所在的用户名
ORA_PARTITION_POS	SQL命令中可以正确添加分区子句位置
ORA_PRIVILEGE_LIST	授予或者回收的权限的数量。
ORA_REVOKEE	被回收者的数量
ORA_SQL_TXT	触发了触发器的SQL语句的行数。
ORA_SYSEVENT	导致DDL触发器被触发的时间
ORA_WITH_GRANT_OPTION	如果授权带有grant选项,返回true。否则false
*/

测试

-- 语句级触发器
create or replace trigger
    t_1
after insert or update or delete
on student
declare
   un varchar(100); 
begin
  select USERNAME into un from user_users;
  if updating then
      dbms_output.put_line(un||'对student表进行了update语句!');
  elsif inserting then
      dbms_output.put_line(un||'对student表进行了insert语句!');
  elsif deleting then
      dbms_output.put_line(un||'对student表进行了delete语句!');
  end if;
end;

drop trigger  t_1;

select * from student;
insert into student values(1,'叶晨','男',99,61.6);
update student set sname='叶星辰' where sname='叶晨';
delete from student where sid=1;
-----------------------------------------------------------------------
-- 行级触发器1
create or replace trigger
    t_2
after insert or update or delete
on student
for each row
declare
    un varchar(100);
begin
  select username into un from user_users;
  if updating then
        dbms_output.put_line(un||'对student表进行了update语句!22');
  elsif inserting then
        dbms_output.put_line(un||'对student表进行了insert语句!22');
  elsif deleting then
        dbms_output.put_line(un||'对student表进行了delete语句!22');
  end if;
end;

drop trigger  t_2;

select * from student;
insert into student values(1,'叶晨','男',99,61.6);
update student set sname='叶星辰' where sname='叶晨';
delete from student where sid=1;
-----------------------------------------------------------------------
-- 行级触发器2
create or replace trigger
       t_3
after insert or update or delete
on student
for each row
declare
    un varchar(100);
begin
    select username into un from user_users;
    if updating then/*有old和new*/
          dbms_output.put_line(un||':student:update:33:旧数据='||:old.sid||'-'||:old.score);
          dbms_output.put_line(un||':student:update:33:新数据='||:new.sid||'-'||:new.score);
    elsif inserting then
          dbms_output.put_line(un||':student:insert:33:新数据='||:new.sid||'-'||:new.score);
    elsif deleting then
          dbms_output.put_line(un||':student:delete:33:旧数据='||:old.sid||'-'||:old.score);
    end if;
end;

drop trigger  t_3;

select * from student;
insert into student values(1,'叶晨','男',99,61.6);
update student set sname='叶星辰' where sname='叶晨';
delete from student where sid=1;
-----------------------------------------------------------------------
-- ddl触发器(模式触发器)   了解
-- 创建一个表记录操作的信息
create table obj(
       oname varchar2(30),
       otype varchar2(20),
       odate date
);
-- 创建触发器:当前用户执行ddl语句时触发
create or replace trigger 
       t_4
after drop or create or alter on schema/*schema模式,对应当前用户下的所以表*/
declare
      un varchar(100);
begin
      select username into un from user_users;
      dbms_output.put_line(un||'执行了ddl语句!');
      insert into  obj  values(ORA_DICT_OBJ_NAME, ORA_DICT_OBJ_TYPE, SYSDATE);    
end;

select * from obj;
select * from tabs;/*查询所有表*/

create table tab_10(tid int primary key,tname varchar(100));
insert into tab_10 values(1,'夕颜');
insert into tab_10 values(2,'若曦');
create table tab_11(tid int primary key,tname varchar(100),tsex varchar(100));
insert into tab_11 values(1,'夕颜','男');
insert into tab_11 values(2,'若曦','男');
-----------------------------------------------------------------------
-- oracle的导入和导出
-- 注意必须在cmd中写命令
exp wen/123@ORCL  tables=(tab_11,tab_10) file='F:\1.dmp';  --导出指定表
exp wen/123@ORCL  full=y inctype=complete  file='F:\1.dmp';  --导出整个用户的所有对象和数据
imp wen/123@ORCL  file='e:\1.dmp' tables=(tab_11,tab_10);

面试题

--oracle面试题
--1 oracle和mysql的区别
--2 数据库优化
--3 oracle的导入导出
--4 oracle和mysql实现分页
--5 oracle序列
--6 函数和存储过程的区别
--7 存储过程和触发器的区别
--8 什么是视图  是否可以对视图解析增删改
--9 索引 
posted @ 2021-12-04 21:53  RenVei  阅读(76)  评论(0编辑  收藏  举报