PL/SQL学习 (部分语法/创建函数/储存过程/游标等)

PL/SQL

  • PL/SQL 是对 SQL 语言存储过程语言的扩展

  • 它现在已经成为 一种过程处理语言,简称 PL/SQL。

  • (有些类似普通编程语言与sql的结合)

PL/SQL 块

  • PL/SQL 程序由三个块组成,即声明部分、执行部分、异常处理部分
DECLARE 
/* 声明部分: 在此声明 PL/SQL 用到的变量,类型及游标,以及局部的存储过程和函数 */
BEGIN
 /* 执行部分: 过程及 SQL 语句 , 即程序的主要部分 */
EXCEPTION
 /* 执行异常部分: 错误处理 */
END;

PL/SQL 块可以分为三类:

  1. 无名块:动态构造,只能执行一次。
  2. 子程序:存储在数据库中的存储过程、函数及包等。当在数据库上建立好后可以在其它程序中调用它们
  3. 触发器:当数据库发生操作时,会触发一些事件,从而自动执行相应的程序。

例子:

declare 
	type test_rec is record( 
        l_name varchar2(30), 
        d_id number(4)     ),
        V_emp test_rec; 
begin 
        vemp.l_name:=Tom:
        vemp.d_id:=1234;
        
        select last_name,department_id 
        into v_emp 
        from employees 
        where employee_id=200;
        
        dbms_output.put_line(v emp.l_name ||','|| v emp.d_id):
end;
  • plsql内的变量类型,大多数与数据库列的数据类型一样。

PL/SQL学习 (部分语法/创建函数/储存过程/游标等)_第1张图片

PL/SQL学习 (部分语法/创建函数/储存过程/游标等)_第2张图片

  • plsql内的赋值号为 :=

  • 程序中 type test_rec is record(……)是记录类型, 记录类型是把逻辑相关的数据作为一个单元存储起来,称作 PL/SQL RECORD 的域(FIELD),其作用是存放互不相同但逻辑相关的信息

  • 定义记录类型语法如下:

TYPE record_type IS RECORD(
 Field1 type1 [NOT NULL] [:= exp1 ],
 Field2 type2 [NOT NULL] [:= exp2 ],
 . . . . . .
 Fieldn typen [NOT NULL] [:= expn ] ) ;
  • 对于select …… into v_emp……可以用 SELECT 语句对记录变量进行赋值,只要保证记录字段与查询结果列表中的字段相配即可。

  • DBMS_OUTPUT.PUT_LINE 过程的功能类似于 Java 中的 System.out.println()直接将输出结果送到 标准输出中.

    • 在使用上述过程之前必须将 SQL * PLUS的环境参数 SERVEROUTPUT 设置为 ON, 否则将看不 到输出结果: set serveroutput on
使用%TYPE

对于上面例子中记录类型内,除了明确写清是哪个类型,还可以通过%type来写

例如

declare
  type test_rec is record (
      l_name employees.last_name%type,
      d_id employees.department_id%type );
  V_emp test_rec;
begin
	
end;
	

使用%TYPE 特性的优点在于:

  • 所引用的数据库列的数据类型可以不必知道;

  • 所引用的数据库列的数据类型可以实时改变。

使用%ROWTYPE

​ PL/SQL 提供%ROWTYPE 操作符, 返回一个记录类型, 其数据类型和数据库表的数据结构相一致。

例如:

declare 
	v_emp empoyees%rowtype;

使用%ROWTYPE 特性的优点在于:

  1. 所引用的数据库中列的个数和数据类型可以不必知道;
  2. 所引用的数据库中列的个数和数据类型可以实时改变。
标识符

PL/SQL 程序设计中的标识符定义与 SQL 的标识符定义的要求相同。

要求和限制有:

  • 标识符名不能超过 30 字符;
  • 第一个字符必须为字母;
  • 不分大小写;
  • 不能用’-‘(减号);
  • 不能是 SQL 保留字。
  • 提示: 一般不要把变量名声明与表中字段名完全一样,如果这样可能得到不正确的结果

PL/SQL学习 (部分语法/创建函数/储存过程/游标等)_第3张图片

练习

1

  • 创建函数,输入参数n, 计算1!+2!+ 3!+ …+ n! 的值。

预备知识

* plsql的函数创建语法
CREATE [OR REPLACE] FUNCTION function_name

[ (argment [ { IN | IN OUT }] Type,
 argment [ { IN | OUT | IN OUT } ] Type ]
[ AUTHID DEFINER | CURRENT_USER ]

RETURN return_type 
{ IS | AS }
<类型.变量的说明>
BEGIN
FUNCTION_body
EXCEPTION<可选的异常错误处理程序>
其它语句
END;

说明:

  1. OR REPLACE 为可选. 有了它, 可以或者创建一个新函数或者替换相同名字的函数, 而不会出现冲突

  2. 函数名后面是一个可选的参数列表, 其中包含 IN, OUT 或 IN OUT 标记. 参数之间用逗号隔开. IN 参数
    标记表示传递给函数的值在该函数执行中不改变; OUT 标记表示一个值在函数中进行计算并通过该参
    数传递给调用语句; IN OUT 标记表示传递给函数的值可以变化并传递给调用语句. 若省略标记, 则参数
    隐含为 IN。

  3. 因为函数需要返回一个值, 所以 RETURN 包含返回结果的数据类型.

* plsql循环语法
  1. WHILE 循环
    WHILE <布尔表达式> LOOP
    要执行的语句;
    END LOOP;
DECLARE 
x NUMBER :=1;
BEGIN
 WHILE x<=10 LOOP
 DBMS_OUTPUT.PUT_LINE('X 的当前值为:'||x);
 x:= x+1;
 END LOOP;
END;
  1. 数字式循环
FOR 循环计数器 IN [ REVERSE ] 下限 .. 上限 LOOP
要执行的语句;
END LOOP;

每循环一次,循环变量自动加 1;使用关键字 REVERSE,循环变量自动减 1。跟在 IN REVERSE 后面的数字必
须是从小到大的顺序,而且必须是整数,不能是变量或表达式。可以使用 EXIT 退出循环。
例 :

BEGIN
 FOR int in 1..10 LOOP
 DBMS_OUTPUT.PUT_LINE('int 的当前值为: '||int);
 END LOOP;
END;

答案

由此:

create or replace function factorial(
      n number)--参数n
      return number --返回值类型为number
      is
             v_sum number;
             v_num number;
begin
      v_sum:=0;
      FOR i IN 1..n LOOP
          v_num:=1;
          For j IN 1..i LOOP
              v_num:=v_num*j;
          end loop;
          v_sum:=v_sum+v_num;
      end loop;
      return v_sum;
end;

测试:

SQL> begin
  2    dbms_output.put_line(factorial(5));
  3  end;
  4  /

153
PL/SQL procedure successfully completed

2

  • 创建存储过程,根据调用时提供的学生姓名查询该学生所修课程的课程信息,在过程体中将课程号、课程名和成绩输出到输出窗口,在SQL窗口中给出过程调用语句块。

** 预备知识**

输出到窗口的语法
set serveroutput on;

设置环境变量serveroutput为打开状态,从而使得pl/sql程序能够在SQL*plus中输出结果

dbms_output.put_line()
--使用此函数可输出内容到窗口
创建储存过程的语法

建立存储过程

oracle函数和存储过程最大的区别就在于,函数必须带上一个return返回值,后面跟的是返回值的类型,而存储过程可以不带任何返回值。
ORACLE SERVER 上建立存储过程,可以被多个应用程序调用,可以向存储过程传递参数,也可以向存储过程传回参数

CREATE [OR REPLACE] PROCEDURE Procedure_name
[ (argment [ { IN | IN OUT }] Type,
 argment [ { IN | OUT | IN OUT } ] Type ]
[ AUTHID DEFINER | CURRENT_USER ]
{ IS | AS }
<类型.变量的说明>
BEGIN
<执行部分>
EXCEPTION
<可选的异常错误处理程序>
END;
oracle存储过程----游标(cursor)的学习

这篇文章写得很详细

练习答案
CREATE OR REPLACE PROCEDURE find_name(
       v_name student_lzq.sname%type)
is
--创建游标
cursor c_cursor is select s.cno,c.cname,s.grade
                 from student_lzq t,sc_lzq s,course_lzq c
                 where t.sname=v_name and s.sno=t.sno and s.cno=c.cno;
cur c_cursor%rowtype;--定义游标变量,该变量的类型为基于游标c_cursor的记录

BEGIN
  --For 循环  for 循环这样的遍历会自动给我们打开游标,并在结束后关闭游标
  
  for cur in c_cursor loop            --从游标c_cursor 中取出一个结果cur
    exit when c_cursor%notfound;      --退出条件exit when c_cursor%notfound; 
                                      --即当游标c_cursor 中没有数据了,就退出循环
    DBMS_OUTPUT.PUT_LINE('数据是:'||cur.cno||'--'||cur.cname||'--'||cur.grade);
  end loop;
END;

写法2:通过fetch

CREATE OR REPLACE PROCEDURE find_name(
       v_name student_lzq.sname%type)
is
--创建游标
cursor c_cursor is select s.cno,c.cname,s.grade
                 from student_lzq t,sc_lzq s,course_lzq c
                 where t.sname=v_name and s.sno=t.sno and s.cno=c.cno;
cur c_cursor%rowtype;--定义游标变量,该变量的类型为基于游标c_cursor的记录

BEGIN
  --Fetch 循环
  open c_cursor; --必须要明确的打开和关闭游标
  LOOP 
        FETCH c_cursor INTO cur;
        EXIT WHEN c_cursor%NOTFOUND;
        --循环体
        DBMS_OUTPUT.PUT_LINE('数据是:'||cur.cno||'--'||cur.cname||'--'||cur.grade);
  END LOOP;
  CLOSE c_cursor;
END;

写法3:通过while循环

CREATE OR REPLACE PROCEDURE find_name(
       v_name student_lzq.sname%type)
is
--创建游标
cursor c_cursor is select s.cno,c.cname,s.grade
                 from student_lzq t,sc_lzq s,course_lzq c
                 where t.sname=v_name and s.sno=t.sno and s.cno=c.cno;
cur c_cursor%rowtype;--定义游标变量,该变量的类型为基于游标c_cursor的记录

BEGIN
  --while 循环
  open c_cursor; --必须要明确的打开和关闭游标
  FETCH c_cursor INTO cur;
  while c_cursor%FOUND LOOP
        --循环体
        DBMS_OUTPUT.PUT_LINE('数据是:'||cur.cno||'--'||cur.cname||'--'||cur.grade);
        FETCH c_cursor INTO cur;
  END LOOP;
  CLOSE c_cursor;
END;
  • fetch loop 与while 都需要明确写出游标的打开与关闭,而for循环则可自动完成,相对便捷。

3

  • 创建存储过程,统计指定学生学号的平均成绩和选课门数,将统计结果用输出参数传递给主程序,在SQL窗口中调用存储过程,输出过程的返回结果。
  • (此题与上题类似,还要在简单点,主要就是参数列表里的 in 与 out的使用了)
CREATE OR REPLACE PROCEDURE test_procedure(
       v_sno in student_lzq.sno%type,
       nc out number,
       x out number
       )
as
begin
  select count(*),avg(s.grade) into nc,x
  from sc_lzq s where s.sno=v_sno group by s.sno;
  exception
    when No_data_found then 
      dbms_output.put_line('查无此人:'||v_sno);
    when others then
      dbms_output.put_line('其他错误');
end;

调用 测试:

DECLARE
nc number;
x number;
begin
test_procedure(2013460422,nc,x);
dbms_output.put_line('选课数:'||nc||' 平均分:'||x);
end;

结果

选课数:4 平均分:91.75

PL/SQL procedure successfully completed

你可能感兴趣的:(数据库学习,sql,数据库,plsql)