oracle存储过程简单语法及例子、oracle的异常、oracle游标

Qracle PL/SQL
--存储过程
1.PL/SQL

  SQL是数据库普通话,每个数据库在实现SQL国际标准之外,也有自己特有的语句。(Hibernate dialect)
  通讯的标准:ODBC(ado , ado.net),JDBC(jdo , Hibernate)
              ODBC、JDBC是最有效率的,但是开发繁琐,才有后来括号中的高度函数化的拓展

  PL/SQL:Procudural Language Extension to SQL.
         在SQL语句基础上,加上了结构化流程控制,可用多条SQL语句完成一个功能。
         PL/SQL功能强大,经常用来书写复杂的业务逻辑。
  语法:
  	set serveroutput on;	//打开控制台输出
	declare [变量名] [变量数据类型];  //定义变量,写在begin之前
	begin
	    //代码块
	end;     //代码编辑区
	:=    //赋值符号 
	dbms_output.put_line('内容'||变量);   //控制台输出,和Java不同的是连接使用的是||符号而不是加

号,注意字符串必须用单引号来修饰

  范例:
	declare result int;
	begin
	  select x into result from ttt where rownum=1;
	  result:=result+1;
	  dbms_output.put_line('result='||result);
	end;


学生表的创建
create table my_student
(
  stuno int primary key,
  stuname varchar2(20) not null,
  stuage  int not null
)
范例2
declare
  v_stuname varchar2(20);
  v_stuage int;
begin
  select stuname, stuage into v_stuname,v_stuage
  from my_student
  where stuno=2;
  dbms_output.put_line('sutdent name'||v_stuname||', student age:'||v_stuage);
  
  exception
    when NO_DATA_FOUND then
      dbms_output.put_line('There is no student with stu no 2');
end; 

范例3:
new -> program window ->procedure

--根据学号显示对应学生信息
create or replace procedure uuu_show_student(x_stuno in int) 
is
  v_stuname varchar2(20);
  v_stuage int;
begin
  select stuname, stuage into v_stuname,v_stuage
  from my_student
  where stuno=x_stuno;
  dbms_output.put_line('sutdent name'||v_stuname||', student age:'||v_stuage);
  
  exception
    when NO_DATA_FOUND then
      dbms_output.put_line('There is no student with stu no 2');
        
end uuu_show_student;

运行存储过程
执行execute uuu_show_student;

范例4.两数求和
create or replace procedure show_uuu_number(x_num1 in int,x_num2 in int)
is
x_big_number int;
x_num_index int;
begin
  if x_num1 > x_num2 then
     x_big_number := x_num1;
     x_num_index := 1;
  else
     x_big_number := x_num2;
     x_num_index := 2;
  end if;
  
  dbms_output.put_line('big number:'||x_big_number||' index:'||x_num_index);
end show_uuu_number;

范例5.循环分支

create or replace procedure show_uuu_number(x_num1 in int,x_num2 in int)
is
x_big_number int;
x_num_index int;
begin
  if x_num1 > x_num2 then
     x_big_number := x_num1;
     x_num_index := 1;
  elsif x_num1 > x_num2 then
     x_big_number := x_num2;
     x_num_index := 2;
  else 
     dbms_output.put_line('equal');
  end if;
  
  if x_num1 <> x_num2 then  
     dbms_output.put_line('big number:'||x_big_number||' index:'||x_num_index);
  end if;
end show_uuu_number;


》case
     when....
     when....
     else
        .....
  and case;

范例6
有返回值的用function

计算三角形面积
create or replace function uuu_area(width in number,height in number) return number is
  Result number;
begin
  Result := 0.5*width*height;
  return(Result);
end uuu_area;
运行函数
select uuu_area(3,5) from dual;

uuu_AREA(3,5)
-------------
           15

范例7
》case
     when....
     when....
     else
        .....
  and case;
应用

create or replace function show_uuu_day(datestr in varchar2)
  return varchar2 is
  Result    varchar2(200);
  checkdate date;
  v_day     varchar2(1);
begin
  checkdate := to_date(trim(datestr), 'YYYY-MM-DD');
  v_day     := to_char(checkdate, 'D');

  case v_day
    when '1' then
      Result := datestr || '是星期天';
    when '2' then
      Result := datestr || '是星期一';
    when '3' then
      Result := datestr || '是星期二';
    when '4' then
      Result := datestr || '是星期三';
    when '5' then
      Result := datestr || '是星期四';
    when '6' then
      Result := datestr || '是星期五';
    when '7' then
      Result := datestr || '是星期六';
    else
      dbms_output.put_line(datestr || '是星期六');
  end case;

  return(Result);
end show_uuu_day;

执行函数
SQL> select show_uuu_day('2010-11-4') from dual;
 
SHOW_uuu_DAY('2010-11-4')
--------------------------------------------------------------------------------
2010-11-4是星期四

范例8
循环结构

create or replace function uuu_get_sum(num1 in number, num2 in number) return number is
  Result number := 0;
  temp number;
begin
  temp := num1;
  loop
    Result:=Result+temp;
    temp:=temp+1;
   -- if temp > num2 then
   --   exit;
    --end if;
  exit when temp > num2;
  end loop;
  return(Result);
end uuu_get_sum;

执行函数
SQL> select uuu_get_sum(1,100) from dual;
 
uuu_GET_SUM(1,100)
------------------
              5050

while loop 
例:
create or replace function uuu_get_sum(num1 in number, num2 in number) return 

number is
  Result number := 0;
  temp number;begin
  temp := num1;
  while num2 <= 200 loop
    Result:=Result+temp;
    temp:=temp+1;
   -- if temp > num2 then
   --   exit;
    --end if;
  exit when temp > num2;
  end loop;  return(Result);
end uuu_get_sum;

执行函数
SQL> select uuu_get_sum(1,1000) from dual;
 
uuu_GET_SUM(1,1000)
-------------------
                  0


范例8
数字for loop循环

create or replace procedure sum(begin1 number,end2 number)
as 
  tosum number;
begin
 tosum:=0;
 for i in begin1..end2
 loop
    tosum:=tosum+i;
 end loop;
 dbms_output.put_line(tosum);
end sum;


001.判断一个数是否为质数

create or replace function x_isprime(num in int) return int is
  Result int;
begin
for i in 2..num-1
   loop
    if num mod i = 0 then
        Result := 0;
        return(Result);
    end if;    
  end loop;
  if num >= 2 then 
    Result := 1;
  end if;
  return(Result);
end x_isprime;

002判断质数

create or replace procedure x_prime(begini in int, endi in int) 
is
count2 int := 0;
begin
  for i in begini..endi
  loop
    if x_isprime(i) = 1 then
     dbms_output.put(i||'  ');
     count2:=count2+1;
     if count2 mod 8 = 0 then
        dbms_output.put_line('');
     end if;
    end if;
  end loop;
end x_prime;


======
debug 权限设置
grant debug connect session to test1;

003判断质数
=======
CREATE OR REPLACE PROCEDURE show_prime(bval IN INT, eval IN INT) IS
  flag INT;
  icount int:=0;
BEGIN
  -- 取数循环
  FOR i IN bval .. eval LOOP
    -- 质数判断循环
    flag := 1;
    FOR j IN 2 .. i - 1 LOOP
      IF i MOD j = 0 THEN
        flag := 0;
        EXIT;
      END IF;
    END LOOP;
  
    IF flag = 1 THEN
      dbms_output.put(i||'  ');
      icount:=icount+1;
      if icount mod 8 =0 then
         dbms_output.put_line('');
      end if;
    END IF;
  
  END LOOP;
END show_prime;

======================================================================
--游标
pl/sql cursor 操作

1. 什么是游标?
   oracle在执行一条SQL语句的时候,它将创建一个内存区域 (context area),该内存区域包含执行这条语句所需要的所有信息。
   信息如下:
     1. 该语句执行之后返回的记录集
     2. 一个指针,指向了该语句在内存中的被解析后的结果。

   cursor(游标) 是一个handle (pointer), 指向了这个上下文区域。

    
   通过cursor, PL/SQL程序能够控制context area, 掌握在语句运行的时,将如何对该区域产生影响。

2. 游标的类型
   1) implicit cursor
      每条SQL语句执行的时候,将自动产生一个implicit游标。 该游标,用户不可控制。

      一个cursor将自动和每条DML语句关联 (update,delete,insert), 我们可以通过cursor了解上头语句产生的结果。

      所有update和delete语句相关联cursor,包含了该操作影响的行的集合。

      最后打开的cursor, 名字叫SQL cursor.

      --------------------------------------------------------------------------------------------
      update my_student set stuname='mary' where stuno=60;
      dbms_output.put_line(SQL%ROWCOUNT);

   2) explicit cursor
      用户自己定义的游标,针对的是返回超过一条记录的查询。 用户可以通过该cursor控制记录返回过程。



      Record Type
      记录是复杂的数据结构。记录往往表现成为表的一行。
create or replace procedure show_student2 is
   vr_student my_student%ROWTYPE;
begin
   select * 
   into vr_student
   from my_student where stuno=6;
   
   dbms_output.put_line(vr_student.stuno||','||vr_student.stuname);
   
end show_student2;
      

      a. 声明游标 (declare a cursor)
         该操作初始化这个游标,为其创建内存空间。
         
         CURSOR c_cursor_name is select statement
         (该游标和select语句相关联)
      
         ------------------
         declare 
           v_name varchar2(20);
           CURSOR c_mycursor is 
              select * from student where name like '%h%';

         游标打开后,不能继续再二次打开。

      
      b. 打开游标 (open cursor)
         创建context area, 执行语句, 获得rows.         

           open c_mycursor

      c. 获取cursor中的行。   
         
         fetch cursorname into pl/sql variables
         fetch cursorname into pl/sql record

      d. 关闭cursor
         一旦所有的行被处理结束,cursor应该被关闭。 
         关闭的操作通知pl/sql engine, 程序对该cursor的需求已经结束,可以释放context are所占用的内存资源。   


         cursor一旦关闭,则不可以继续fetch . 也不能重复关闭cursor.

    > 常用的cursor属性
      cursorname%NOTFOUND
      cursorname%FOUND
      cursorname%ROWCOUNT
      cursorname%ISOPEN

 --------------------
create or replace procedure show_student2 is
   CURSOR c_student is 
       select * from my_student order by stuno desc;
   vr_student my_student%ROWTYPE;
   
   TYPE simple_stu is record
     (
       stuname my_student.stuname%TYPE,
       stuage  my_student.stuage%TYPE,
       stuage2 int
      );
    vr_simple_student simple_stu;
    cursor c_simple_student is
        select stuname,stuage,stuage+2 from my_student order by stuno desc;
begin
/*
   select * 
   into vr_student
   from my_student where stuno=6;
   
   dbms_output.put_line(vr_student.stuno||','||vr_student.stuname);
*/
  
   open c_student;
    
   loop
     fetch c_student into vr_student;
     exit when c_student%NOTFOUND;
     dbms_output.put_line(vr_student.stuno||','||vr_student.stuname);
   end loop;
   
   if c_student %ISOPEN then 
      close c_student;   
   end if;
   
   open c_simple_student;
   loop
     fetch c_simple_student into vr_simple_student;
     exit when c_simple_student%NOTFOUND;
     dbms_output.put_line(vr_simple_student.stuname||','||vr_simple_student.stuage
                                 ||','||vr_simple_student.stuage2);
   end loop;
   close c_simple_student;
   
end show_student2;

--------------------------------------------

cursor for loop 
nested cursor

DECLARE
  v_sid student.student_id%TYPE;
  CURSOR c_student IS
    SELECT student_id, first_name, last_name
      FROM student
     WHERE student_id < 110;
  CURSOR c_course IS
    SELECT c.course_no, c.description
      FROM course c, section s, enrollment e
     WHERE c.course_no = s.course_no
       AND s.section_id = e.section_id
       AND e.student_id = v_sid;
BEGIN
  /*
   使用for loop cursor, 可以便捷的遍历游标,省去了open,fetch,close的书写。
    连保存变量的定义也可以省略,可在for后直接书写变量名。
  */
  FOR r_student IN c_student LOOP
    v_sid := r_student.student_id;
    DBMS_OUTPUT.PUT_LINE(chr(10));
    DBMS_OUTPUT.PUT_LINE(' The Student ' || r_student.student_id || ' ' ||
                         r_student.first_name || ' ' ||
                         r_student.last_name);
    DBMS_OUTPUT.PUT_LINE(' is enrolled in the ' || 'following courses: ');
    -- nested cursor
    FOR r_course IN c_course LOOP
      DBMS_OUTPUT.PUT_LINE(r_course.course_no || ' ' ||
                           r_course.description);
    END LOOP;
  END LOOP;
END;

---------------------------------------------------------------
带参数的游标

   CURSOR c_student(p_stuage in my_student.stuage%type) is 
       select * from my_student where stuage=p_stuage order by stuno desc;
 

   for vr_student in c_student(20) 
   loop
     dbms_output.put_line(vr_student.stuno||','||vr_student.stuname);
   end loop;

  CURSOR c_student2(p_stuage in my_student.stuage%type,p_stuname in my_student.stuname%TYPE) is 
       select * from my_student where stuage=p_stuage order by stuno desc;
   
cursor具备的参数:
 1) cursor得到了复用。
 2) 提高了性能,压缩了返回的行的数量。

====================================================================

--异常
1.PL/SQL Exception
  常用的预定义异常
  a)NO_DATA_FOUND
    根据查询条件,没有查询记录被找到
  b)TOO_MANY_ROWS
     select into 结构只能返回一条记录,赋予存储过程变量。如果select              ..into..结构返回记录为多条,将产生这个异常 
  c)ZERO_DIVIDE
    除数是0(把ORA-01476 error映射成ZERO_DIVIDE错误)
    例: SQL> select 6/0 from dual;
      select 6/0 from dual
      ORA-01476: 除数为 0
  d)VALUE_ERROR
    从运算或者数据库中取得值赋予变量的时候类型不匹配或者长度不足,导致的异常
  e)DUP_VAL_ON_INDEX
      主键不可重复,违反主键唯一约束
  f)OTHERS
     (类似java Exception异常)

数据库表
create table my_student(
  stuno int primary key,
  stuname varchar2(20),
  stuage int
);
insert into my_student values(1,'dadiv',20);
insert into my_student values(2,'mary',20);
insert into my_student values(3,'henry',20);
异常举例例子:
create or replace procedure uuu_show_student(x_stuno in int)
is
  v_stuname varchar2(20);--替换v_stuname varchar2(2)/int值不匹配或者长度不足异常
  v_stuage int;
begin
  insert into my_student values(2,'kate',49);--DUP_VAL_ON_INDEX异常
  select stuname, stuage into v_stuname,v_stuage
  from my_student
  where stuno=x_stuno;--加上or stuname like '%y'产生值记录太多异常
  dbms_output.put_line('sutdent name'||v_stuname||', student age:'||v_stuage);
  
  exception
    when NO_DATA_FOUND then
      dbms_output.put_line('There is no student with stu no '||x_stuno);
    when VALUE_ERROR then
      dbms_output.put_line('值不匹配');
    when TOO_MANY_ROWS then
      dbms_output.put_line('记录太多');
    when DUP_VAL_ON_INDEX then
      dbms_output.put_line('主键不可重复,插入失败');
    when OTHERS then 
      dbms_output.put_line('其它异常捕获');--一些预定义异常的父类
        
end uuu_show_student;
    

异常的作用域

  DECLARE
    v_student_id NUMBER := &sv_student_id;
    v_name VARCHAR2(30);
    v_total NUMBER(1);
    -- outer block
    
    BEGIN
      SELECT RTRIM(first_name)||' '||RTRIM(last_name)
      INTO v_name
      FROM student
      WHERE student_id = v_student_id;
      DBMS_OUTPUT.PUT_LINE ('Student name is '||v_name);
      -- inner block
      BEGIN
        SELECT COUNT(*)
        INTO v_total
        FROM enrollment
        WHERE student_id = v_student_id;
        DBMS_OUTPUT.PUT_LINE ('Student is registered for '||
        v_total||' course(s)');
        EXCEPTION
        WHEN VALUE_ERROR OR INVALID_NUMBER THEN
        DBMS_OUTPUT.PUT_LINE ('An error has occurred');
      END;
      
      EXCEPTION
      WHEN NO_DATA_FOUND THEN
      DBMS_OUTPUT.PUT_LINE ('There is no such student');
END; 

自定义异常:

例:
create or replace procedure uuu_show_student(x_stuno in int)
is
  v_stuname varchar2(20);--替换v_stuname varchar2(2)/int值不匹配或者长度不足异常
  v_stuage int;
  e_invalid_stuno EXCEPTION;--自定义异常
begin
  if x_stuno < 0 then
     raise e_invalid_stuno;
  else
    --insert into my_student values(2,'kate',49);--DUP_VAL_ON_INDEX异常
    select stuname, stuage into v_stuname,v_stuage
    from my_student
    where stuno=x_stuno;--加上or stuname like '%y'产生值记录太多异常
    dbms_output.put_line('sutdent name'||v_stuname||', student age:'||v_stuage);
  end if;
  exception
    when NO_DATA_FOUND then
      dbms_output.put_line('There is no student with stu no '||x_stuno);
    when VALUE_ERROR then
      dbms_output.put_line('值不匹配');
    when TOO_MANY_ROWS then
      dbms_output.put_line('记录太多');
    when e_invalid_stuno then       --控制台输入execute uuu_show_student(-2);
      dbms_output.put_line('学生编号不合法');
    when DUP_VAL_ON_INDEX then
      dbms_output.put_line('主键不可重复,插入失败');
    when OTHERS then 
      dbms_output.put_line('其它异常捕获');--一些预定义异常的父类
        
end uuu_show_student;
    

==================================================================

--触发器的一个例子
create or replace trigger student_aud
  before insert on my_student  
  for each row
declare
  v_highage int;
begin
  
  select stu_highage
  into v_highage
  from stu_stat;
  
  
  if :NEW.stuage >25 then
     v_highage:=v_highage+1;
  end if ;
  
  update stu_stat set stu_count=stu_count+1,stu_highage=v_highage;
  
end student_aud; 

你可能感兴趣的:(oracle,sql,C++,c,C#)