(数据库)16_PL_SQL

一、PL/SQL

SQL语言只是访问、操作数据库的语言,并不是一种具有流程控制的程序设计语言,而只有程序设计语言才能用于应用软件的开发。
PL /SQL是一种高级数据库程序设计语言,该语言专门用于在各种环境下对ORACLE数据库进行访问。由于该语言集成于数据库服务器中,所以PL/SQL代码可以对数据进行快速高效的处理。除此之外,可以在ORACLE数据库的某些客户端工具中,使用PL/SQL语言也是该语言的一个特点。

1. 什么是PL/SQL?

PL/SQL是 Procedure Language & Structured Query Language 的缩写。PL/SQL是对SQL语言存储过程语言的扩展。从ORACLE6以后,ORACLE的RDBMS附带了PL/SQL。它现在已经成为一种过程处理语言,简称PL/SQL。目前的PL/SQL包括两部分,一部分是数据库引擎部分;另一部分是可嵌入到许多产品(如C语言,JAVA语言等)工具中的独立引擎。可以将这两部分称为:数据库PL/SQL和工具PL/SQL。两者的编程非常相似。都具有编程结构、语法和逻辑机制。工具PL/SQL另外还增加了用于支持工具(如ORACLE Forms)的句法,如:在窗体上设置按钮等。

2. PL/SQL的优点或特征

  1. 有利于客户/服务器环境应用的运行
  2. 适合于客户环境
  3. 过程化
  4. 模块化
  5. 运行错误的可处理性
  6. 提供大量内置程序包

3. 运行PL/SQL程序

PL/SQL程序的运行是通过ORACLE中的一个引擎来进行的。这个引擎可能在ORACLE的服务器端,也可能在 ORACLE 应用开发的客户端。引擎执行PL/SQL中的过程性语句,然后将SQL语句发送给数据库服务器来执行。再将结果返回给执行端。

二、PL/SQL块

PL/SQL程序由三个块组成,即声明部分、执行部分、异常处理部分。
PL/SQL块的结构如下:

DECLARE  
/* 声明部分: 在此声明PL/SQL用到的变量,类型及游标,以及局部的存储过程和函数 */
BEGIN
    /*  执行部分:  过程及SQL 语句  , 即程序的主要部分  */
EXCEPTION
   /* 执行异常部分: 错误处理  */
END;
其中 执行部分是必须的。

PL/SQL块可以分为三类:

  1. 无名块:动态构造,只能执行一次。
  2. 子程序:存储在数据库中的存储过程、函数及包等。当在数据库上建立好后可以在其它程序中调用它们。
  3. 触发器:当数据库发生操作时,会触发一些事件,从而自动执行相应的程序。
declare
  str varchar2(30);
  a number;
 begin
   str := 'hello world';
   dbms_output.put_line(str);
 end;
declare
  str varchar2(30);
  a number;
 begin
   str := 'hello world';
   a := 'aaa';
   dbms_output.put_line(str);
   dbms_output.put_line(a);
   exception
     when others  then
       dbms_output.put_line('输入有误');
 end;

1.变量的声明

1.1.标识符

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

标识符名不能超过30字符;
第一个字符必须为字母;
不分大小写;
不能用’-‘(减号);
不能是SQL保留字。

1.2.变量命名

在这里插入图片描述

2.变量类型

2.1.基本类型

在这里插入图片描述

2.2.RECORD 类型

记录类型
记录类型是把逻辑相关的数据作为一个单元存储起来,它必须包括至少一个标量型或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 ] ) ;
 -- Type recode 
 declare
    type person is record(
         pname varchar2(30),
         page number,
         psex varchar2(10)
    ) ;--自定义类型
    V_per  person;--声明了一个自定义类型的变量
 begin
   V_per.pname := '张三';
   V_per.page :=22;
   V_per.psex :='男';
   dbms_output.put_line(V_per.pname || '---'||V_per.page||'--'||V_per.psex);
 end;

2.3.使用%TYPE

定义一个变量,其数据类型与已经定义的某个数据变量的类型相同,或者与数据库表的某个列的数据类型相同,这时可以使用%TYPE。
使用%TYPE特性的优点在于:

所引用的数据库列的数据类型可以不必知道;
所引用的数据库列的数据类型可以实时改变。
 --%Type
 declare
    V_ename emp.first_name%Type;
    V_sal  emp.salary %Type;
    V_date emp.hire_date%Type;
 
 begin
   select first_name,salary,hire_date into V_ename,V_sal,V_date
   from emp where employee_id =100; 
 
   dbms_output.put_line(V_ename||'--'||V_sal||'--'||V_date);
 
 end;

2.4.%ROWTYPE

-- %ROWTYPE
declare 
   V_emp emp%Rowtype;--V_emp的类型为emp表的一行

begin
  select first_name,salary ,hire_date into V_emp.first_name,V_emp.salary,V_emp.hire_date
  from emp where employee_id=100;
  dbms_output.put_line(V_emp.first_name||'--'||V_emp.salary);
end;
 

3.运算符

3.1.关系运算符

在这里插入图片描述

3.2.一般运算符

在这里插入图片描述

3.3.逻辑运算符

在这里插入图片描述

4.变量赋值

在PL/SQL编程中,变量赋值是一个值得注意的地方,它的语法如下:

 variable  := expression ;

variable 是一个PL/SQL变量, expression 是一个PL/SQL 表达式.

4.1.字符及数字运算特点

  1. 空值加数字仍是空值:NULL + < 数字> = NULL
  2. 空值加(连接)字符,结果为字符:NULL || <字符串> = < 字符串>

4.2.BOOLEAN 赋值

**布尔值只有TRUE, FALSE及 NULL 三个值。**如:

DECLARE
done BOOLEAN;
BEGIN
done := &done;
 dbms_output.put_line(
     CASE 
        WHEN done IS NULL THEN 'unknow'
        WHEN done THEN 'yes'
         WHEN  NOT done THEN 'no'
     END
 );
END;

4.3. 数据库赋值

数据库赋值是通过 SELECT语句来完成的,每次执行 SELECT语句就赋值一次,一般要求被赋值的变量与SELECT中的列名要一一对应。如:

DECLARE
emp_id    emp.empno%TYPE :=7788;
emp_name emp.ename%TYPE;
wages     emp.sal%TYPE;
BEGIN
SELECT ename, NVL(sal,0) + NVL(comm,0) INTO emp_name, wages 
FROM emp WHERE empno = emp_id;
Dbms_output.put_line(emp_name||'----'||to_char(wages));
END;

提示:不能将SELECT语句中的列赋值给布尔变量。

4.4. 可转换的类型赋值

  1. CHAR 转换为 NUMBER:使用 TO_NUMBER 函数来完成字符到数字的转换,如:
v_total  := TO_NUMBER(100.0) + sal;
  1. NUMBER 转换为CHAR:使用 TO_CHAR函数可以实现数字到字符的转换,如:
v_comm := TO_CHAR(123.45) || ’元’ ;
  1. 字符转换为日期:使用 TO_DATE函数可以实现 字符到日期的转换,如:
v_date := TO_DATE('2001.07.03','yyyy.mm.dd');
  1. 日期转换为字符使用 TO_CHAR函数可以实现日期到字符的转换,如:
v_to_day := TO_CHAR(SYSDATE, 'yyyy.mm.dd hh24:mi:ss') ;

5.PL/SQL编程练习

5.1.简单数据插入例子

--给Student表插入数据
declare
  V_id  student.stu_id%type ;
  V_name student.stu_name%type;
  V_age student.stu_age%type ;
  V_sex student.stu_sex%type ;
  
begin
  insert into student(stu_id,stu_name,stu_age,stu_sex)
  values(&V_id,'&V_name',&V_age,'&V_sex');  
end;

5.2. 简单数据删除例子

--简单数据删除例子
declare 
  V_id student.stu_id%type;

begin
  delete from student where stu_id=&V_id;
  commit;
end;

三、流程控制

PL/SQL的流程控制语句, 包括如下三类:

  1. 控制语句: IF 语句
  2. 循环语句: LOOP语句, EXIT语句
  3. 顺序语句: GOTO语句, NULL语句

1.条件语句

IF <布尔表达式> THEN
PL/SQLSQL语句
END IF;
IF <布尔表达式> THEN
PL/SQLSQL语句
ELSE
其它语句
END IF;
IF <布尔表达式> THEN
PL/SQLSQL语句
ELSIF < 其它布尔表达式> THEN
其它语句
ELSIF < 其它布尔表达式> THEN
其它语句
ELSE
其它语句
END IF;

提示: ELSIF 不能写成 ELSEIF

--根据用户输入的用户编号,确定用户的工资等级
declare
  V_empno emp.employee_id%type := &V_empno;
  V_sal emp.salary %Type;
begin
  select salary into V_sal from emp where employee_id=V_empno; 
  if V_sal >= 10000 then
    dbms_output.put_line('一级');
  elsif V_sal >= 8000 and V_sal <10000 then
      dbms_output.put_line('二级');
   elsif V_sal >=5000 and V_sal <8000 then
     dbms_output.put_line('三级');
   else
      dbms_output.put_line('四级');
    end if;
end;

2.循环语句

2.1.简单循环

LOOP
      要执行的语句;
      EXIT WHEN <条件语句>       /*条件满足,退出循环语句*/
END LOOP;
--根据用户输入的用户编号,确定用户的工资等级
declare
  i number :=1;
begin
  loop
      dbms_output.put_line(i);
      i := i+1;
      exit when i =10;
  end loop;
end;

2.2.WHILE 循环

WHILE <布尔表达式> LOOP
    要执行的语句;
END LOOP;
--根据用户输入的用户编号,确定用户的工资等级
declare
  i number :=1;
begin
  while i < 10 loop
    dbms_output.put_line(i);
    i:= i+1;
    end loop;
end;

2.3.数字式循环

FOR 循环计数器 IN [ REVERSE ] 下限 .. 上限 LOOP
  要执行的语句;
END LOOP;

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

declare
  i number :=1;
begin
  for i in 1..10 loop
    dbms_output.put_line(i);
   end loop;
end;
-- 逆序输出
declare
  i number :=10;
begin
  for i in reverse 1..10 loop
    dbms_output.put_line(i);
   end loop;
end;

2.4. NULL 语句

在PL/SQL 程序中,可以用 null 语句来说明“不用做任何事情”的意思,相当于一个占位符,可以使某些语句变得有意义,提高程序的可读性。如:

declare
  i number :=10;
begin
  for i in reverse 1..10 loop
    if i = 5 then 
      null;--什么都不做  类似于Java中;
    else
      dbms_output.put_line(i);
    end if;
   end loop;
end;

小结

PLSQL:声明部分 主体部分 异常部分

声明部分:声明变量
主体部分:所有的逻辑操作
异常部分:对主体部分发生异常情况进行处理
declare
begin
    exception
end;

变量: 变量名 类型(长度);
数据类型: oracle的基本的数据类型 %Type %RowType
dbms_output.put_line();

控制语句:

  1. 条件语句:
declare
begin
    if 条件表达式  then
    elsif  条件表达式  then
    else
    end if;
end;
  1. 循环语句:

①基本循环:

loop
    循环体;
    exit when 条件;
end  loop;

②while循环:

while 条件表达式  loop
循环体;
end loop;

③数字式循环:

for 变量  in  下限  .. 上线 loop
    循环体
end  loop;

运算符: 赋值运算符 (:= ) 字符串的拼接符 ||

select  字段   into 变量 from  表名  where 条件
--编写一个plsql块  根据员工的工号 来输出该员工的first_name和salary
declare
    V_empno emp.employee_id%type := &V_empno;     
    V_ename emp.first_name%Type;
    V_sal emp.salary %Type;     
begin
  select first_name,salary into V_ename,V_sal from emp where employee_id =V_empno;
  dbms_output.put_line(V_ename||'--'||V_sal);
end;

3.游标

在 PL/SQL 程序中,对于处理多行记录的事务经常使用游标来实现

3.1.游标概念

  1. 为了处理 SQL 语句,ORACLE 必须分配一片叫上下文( context area )的区域来处理所必需的信息,其中包括要处理的行的数目,一个指向语句被分析以后的表示形式的指针以及查询的活动集(active set)。
  2. 游标是一个指向上下文的句柄( handle)或指针。通过游标,PL/SQL可以控制上下文区和处理语句时上下文区会发生些什么事情
    在这里插入图片描述
    在这里插入图片描述

3.2.显式游标

显式游标处理需四个 PL/SQL步骤:

--查询emp表中的所有记录  并输出last_name  ,salary,hire_date
declare
--1 声明一个游标 游标后的select 语句不能出现into 
--包含into的select语句 都返回的时单行记录  
--游标处理的是多行记录
    cursor C_emp is select * from emp;
    V_emp emp%Rowtype;
begin
  --2 打开游标
  open C_emp ;
  --3.提取游标数据 每提取一次  只能获得一行记录
  fetch C_emp into V_emp;
  dbms_output.put_line(V_emp.last_name||'--'||V_emp.salary||'--'||V_emp.hire_date);
  
  fetch C_emp into V_emp;
  dbms_output.put_line(V_emp.last_name||'--'||V_emp.salary||'--'||V_emp.hire_date);
  
  fetch C_emp into V_emp;
  dbms_output.put_line(V_emp.last_name||'--'||V_emp.salary||'--'||V_emp.hire_date);
  --4关闭游标
  close C_emp;
end;
  1. 定义游标:就是定义一个游标名,以及与其相对应的SELECT 语句
    游标中的参数 只能是输入参数 而且游标中的参数不能指定长度
  2. 打开游标:
    就是执行游标所对应的SELECT 语句,将其查询结果放入工作区,并且指针指向工作区的首部,标识游标结果集合。如果游标查询语句中带有FOR UPDATE选项,OPEN 语句还将锁定数据库表中游标结果集合对应的数据行。
    在向游标传递参数时,可以使用与函数参数相同的传值方法,即位置表示法和名称表示法。PL/SQL 程序不能用OPEN 语句重复打开一个游标
    格式:
OPEN cursor_name[([parameter =>] value[, [parameter =>] value])];
  1. 提取游标数据:就是检索结果集合中的数据行,放入指定的输出变量中
    格式:
FETCH cursor_name INTO {variable_list | record_variable };
  1. 关闭游标:当提取和处理完游标结果集合数据后,应及时关闭游标,以释放该游标所占用的系统资源,并使该游标的工作区变成无效,不能再使用FETCH 语句取其中数据。关闭后的游标可以使用OPEN 语句重新打开。
    格式:
CLOSE cursor_name;
-- 游标可以定义参数 查询90号部门的员工
--游标中的参数  只能是输入参数  而且游标中的参数不能指定长度
declare
   cursor c1(dept_no number ) is select * from emp where department_id <= dept_no;
   V_emp emp%ROWType;
begin
  open c1(90);
  fetch c1 into V_emp;
  dbms_output.put_line(V_emp.last_name||'--'||V_emp.salary||'--'||V_emp.hire_date||'--'||V_emp.department_id);
  close c1;  
end;
--查询first_name 中包含A的雇员信息
declare
   cursor c2(ename varchar2 ) is select * from emp where first_name like '%'||ename||'%';
   V_emp emp%ROWType;
begin
  open c2('A');--打开游标  并为游标中的参数赋值
  fetch c2 into V_emp;
  dbms_output.put_line(V_emp.last_name||'--'||V_emp.salary||'--'||V_emp.hire_date||'--'||V_emp.department_id);
  close c2;  
end;

游标中的参数的传值
在打开游标时进行传值

--传值方式一  位置表示法
open c2('A');--打开游标  并为游标中的参数赋值  称为位置表示法
declare
   cursor c3(ename varchar2,dept_no number ) 
   IS
   select * from emp where last_name like '%'||ename||'%' and department_id = dept_no;
   V_emp emp%ROWType;
begin
  open c3('h',90);--位置表示法
  fetch c3 into V_emp;
  dbms_output.put_line(V_emp.last_name||'--'||V_emp.salary||'--'||V_emp.hire_date||'--'||V_emp.department_id);
  close c3;  
end;
--名称表示法
open c1(dept_no=>90);
-- 查询90号部门中姓名中包含字母h的雇员信息
declare
   cursor c3(ename varchar2,dept_no number ) 
   IS
   select * from emp where last_name like '%'||ename||'%' and department_id = dept_no;
   V_emp emp%ROWType;
begin
  open c3( dept_no => 90,ename=>'h');--名称表示法
  fetch c3 into V_emp;
  dbms_output.put_line(V_emp.last_name||'--'||V_emp.salary||'--'||V_emp.hire_date||'--'||V_emp.department_id);
  close c3;  
end;

–混合表示法

-- 查询90号部门中姓名中包含字母h的雇员信息
declare
   cursor c3(ename varchar2,dept_no number ) 
   IS
   select * from emp where last_name like '%'||ename||'%' and department_id = dept_no;
   V_emp emp%ROWType;
begin
  open c3( 'h',dept_no => 90);--混合表示法
  fetch c3 into V_emp;
  dbms_output.put_line(V_emp.last_name||'--'||V_emp.salary||'--'||V_emp.hire_date||'--'||V_emp.department_id);
  close c3;  
end;

游标属性

  1. %FOUND 布尔型属性,当最近一次读记录时成功返回,则值为TRUE;
  2. %NOTFOUND 布尔型属性,与%FOUND相反;
  3. %ISOPEN 布尔型属性,当游标已打开时返回 TRUE;
  4. %ROWCOUNT 数字型属性,返回已从游标中读取的记录数。
    ①使用loop循环提取数据:
--使用循环提取数据
-- 查询90号部门中姓名中包含字母h的雇员信息  
declare
   cursor c3(ename varchar2,dept_no number ) 
   IS
   select * from emp where last_name like '%'||ename||'%' and department_id = dept_no;
   V_emp emp%ROWType;
begin
  open c3( 'h',dept_no => 90);--混合表示法
  loop
  fetch c3 into V_emp;
  
  exit when c3%NOTFOUND;--在loop循环的时候 必须先判断后输出
  dbms_output.put_line(V_emp.last_name||'--'||V_emp.salary||'--'||V_emp.hire_date||'--'||V_emp.department_id);
  end loop;
  close c3;  
end;
 

c3%rowcount 获取游标中的记录的行数

--使用循环提取数据
-- 查询90号部门中的雇员信息  
declare
   cursor c3(dept_no number ) 
   IS
   select * from emp where department_id = dept_no;
   V_emp emp%ROWType;
begin
  open c3( dept_no => 100);--混合表示法
  
  loop
  fetch c3 into V_emp;
  
  exit when c3%NOTFOUND;--在loop循环的时候 必须先判断后输出
  dbms_output.put_line(c3%rowcount||'--'||V_emp.last_name||'--'||V_emp.salary||'--'||V_emp.hire_date||'--'||V_emp.department_id);
  end loop;
  close c3;  
end;

②while循环提取数据(忽略。有缺陷)
③FOR循环:
PL/SQL语言提供了游标FOR循环语句,自动执行游标的OPEN、FETCH、CLOSE语句和循环语句的功能;当进入循环时,游标FOR循环语句自动打开游标,并提取第一行游标数据,当程序处理完当前所提取的数据而进入下一次循环时,游标FOR循环语句自动提取下一行数据供程序处理,当提取完结果集合中的所有数据行后结束循环,并自动关闭游标。
格式:

 FOR index_variable IN cursor_name[value[, value]] LOOP
         -- 游标数据处理代码
    END LOOP;
--使用FOR循环提取数据
-- 查询90号部门中的雇员信息 
-- 使用for循环提取数据  for隐式的包含了打开游标 fetch  关闭游标 
declare
   cursor c3(dept_no number ) 
   IS
   select * from emp where department_id = dept_no;
begin
  for V_emp in c3(100) loop --V_emp 该变量默认 %ROWType;
      dbms_output.put_line(c3%rowcount||'--'||V_emp.last_name||'--'||V_emp.salary||'--'||V_emp.hire_date||'--'||V_emp.department_id);
  end loop;
end;
declare
   cursor c3(dept_no number ) 
   IS
   select * from emp where department_id = dept_no;
   V_emps emp%Rowtype;
begin
  for V_emp in c3(100) loop --V_emp 该变量默认 %ROWType;
      dbms_output.put_line(c3%rowcount||'--'||V_emp.last_name||'--'||V_emp.salary||'--'||V_emp.hire_date||'--'||V_emp.department_id);
  end loop;
  --游标关闭之后不能在提取数据
  --再次提取游标的数据 需要重新打开游标
  
  open c3(90);
  fetch c3 into V_emps;
   dbms_output.put_line(c3%rowcount||'--'||V_emps.last_name||'--'||V_emps.salary||'--'||V_emps.hire_date||'--'||V_emps.department_id);
   close c3;
end;

在一个PL/SQL块中可以定义多个游标

declare
   cursor c3(dept_no number ) 
   IS
   select * from emp where department_id = dept_no;
  cursor c4 
  is
   select * from emp;
begin
  for V_emp in c3(100) loop --V_emp 该变量默认 %ROWType;
      dbms_output.put_line(c3%rowcount||'--'||V_emp.last_name||'--'||V_emp.salary||'--'||V_emp.hire_date||'--'||V_emp.department_id);
  end loop;
   dbms_output.put_line('------------------------------------------');
  for V_emp in c4 loop
   dbms_output.put_line(c4%rowcount||'--'||V_emp.last_name||'--'||V_emp.salary||'--'||V_emp.hire_date||'--'||V_emp.department_id);
   end loop; 
end;

综合练习

--查询所有的雇员信息 并为工资低于10000的员工 增加工资100
declare
     cursor c is
     select * from emp;
begin
     for V_emp in c loop
       if V_emp.salary < 10000 then
          update emp set salary = salary +100 where employee_id = V_emp.employee_id;
         dbms_output.put_line(c%rowcount||'--'||V_emp.last_name||'--'||V_emp.salary||'--'||V_emp.hire_date||'--'||V_emp.department_id);
       end if;
      end loop;    

end;
--查询所有的雇员信息 并为工资低于10000的员工 增加工资100
declare
     cursor c is
     select * from emp;
     V_emp emp%rowtype;
begin
  open c;
    loop 
      fetch  c into V_emp;
          if V_emp.salary < 10000 then
            update emp set salary = salary +100 where employee_id = V_emp.employee_id;
          end if;
       exit when c%notfound;
    end loop;
   close c;     
end;

四、PL/SQL程序单元

是数据库中命名的PL/SQL块,作为数据库对象保存在数据库中。
主要有四类:

过程:执行特定操作,无返回值;
函数:执行复杂操作,有返回值
包:逻辑上相关的过程和函数的组合体
触发器:事件触发,执行相应的操作

ORACLE 提供可以把PL/SQL 程序存储在数据库中,并可以在任何地方来运行它。这样就叫存储过程或函数。
过程和函数统称为PL/SQL子程序,他们是被命名的PL/SQL块,均存储在数据库中,并通过输入、输出参数或输入/输出参数与其调用者交换信息。
过程和函数的唯一区别是函数总向调用者返回数据,而过程则不返回数据。

1.函数和存储过程

函数是一段独立的PL/SQL程序代码,它执行某个特定的、明确的任务。
通常,函数将处理从程序的调用部分传递给它的信息,然后返回单个值。信息通过称为参数的特殊标识符传递给函数,然后通过RETURN语句返回。
语法如下:

CREATE [OR REPLACE] FUNCTION function_name
[(argment [ { IN| IN OUT }] type,
    argment [ { IN | OUT | IN OUT } ] type]
RETURN return_type 
{ IS | AS }
<类型.变量的说明> 
BEGIN
FUNCTION_body
EXCEPTION
其它语句
END;

第一个函数

create or replace  function fun 
return varchar2 
is 
res varchar2(50);
begin
  res:= 'hello plsql';
  return res;
end;

执行函数

declare
  res varchar2(50);
begin
  res :=fun();--调用函数  函数名后的()可选  但是如果有参数 则必须加
  dbms_output.put_line(res);
end;

带参数的函数

--函数中有一个参数 参数是输入参数 参数的类型不能指定长度
create or replace  function fun(str in varchar2) 
return varchar2 
is 
res varchar2(50);
begin
  res:=  str ;
  return res;
end;

函数调用

declare
  res varchar2(50);
begin
  res :=fun('hello world');--调用函数  函数名后的()可选  但是如果有参数 则必须加
  dbms_output.put_line(res);
end;
-- 获取某部门的工资总和 
create or replace function salSum(
       deptno in number,--输入参数
       salSum out number--输出参数
)
return number 
is --如果使用输出参数作为返回值 则返回参数可省略
begin 
  select sum(salary) into salSum from emp where department_id =deptno;
  return salSum;
end ;
declare
V_sum number;
V_s number := 0;
V_deptno number := &V_deptno;
begin
  V_sum :=salSum(V_deptno,V_s);
  dbms_output.put_line(V_sum);
end;

参数的 传递

declare
V_sum number;
V_s number := 0;
V_deptno number := &V_deptno;
begin
  V_sum :=salSum(V_deptno,V_s);--位置表示法
  dbms_output.put_line(V_sum);
end;
declare
V_sum number;
V_s number := 0;
V_deptno number := &V_deptno;
begin
   V_sum :=salSum(deptno =>V_deptno,salSum=>V_s);--名称表示法
  dbms_output.put_line(V_sum);
end;
declare
V_sum number;
V_s number := 0;
V_deptno number := &V_deptno;
begin
  V_sum :=salSum(V_deptno,salSum=>V_s);--混合表示法 位置表示法必须位于名称表示法之前
  dbms_output.put_line(V_sum);
end;

混合表示法:采用这种参数传递方法时,使用位置表示法所传递的参数必须放在名称表示法所传递的参数前面。也就是说,无论函数具有多少个参数,只要其中有一个参数使用名称表示法,其后所有的参数都必须使用名称表示法。
输入输出参数

---- 获取某部门的工资总和 
create or replace function salSum(
       v_i in out number--输入输出参数
 
)
return number 
is --如果使用输出参数作为返回值 则返回参数可省略
begin 
  select sum(salary) into v_i from emp where department_id =v_i;
  return v_i;
end ;

使用

declare
V_s  number := &V_s;
 
begin
  V_s :=salSum(V_s);--混合表示法 位置表示法必须位于名称表示法之前
  dbms_output.put_line(V_s);
end;

既然有返回值 为什么还要有输出参数:

因为函数的返回值  只能返回一个值 在某些情况下 需要返回多个值  输出参数就是解决函数只能返回一个值的问题。
--统计部门人数  以及部门的平均工资 以及入职最早的人
create or replace function f(deptno in number, avgsal out number ,num out number) 
return varchar2 is
 fisrtEmp Date;
begin
  select count(*),avg(salary),min(hire_date) into num,avgsal,fisrtEmp from emp where department_id =deptno;   

  return(fisrtEmp);
end f;
declare 
  v_no number;
  v_avg number;
  v_hiredate date;
  v_num number;
 begin
   v_hiredate := f(90,v_avg,v_num);
   dbms_output.put_line(v_avg);
     dbms_output.put_line(v_hiredate);
       dbms_output.put_line(v_num);
 end;

2.过程

和函数的唯一区别:没有返回值
语法:

CREATE  OR  REPLACE  PROCEDURE  过程名  [(parameter,...)]
IS
    定义变量
Begin
    Plsql程序
End;

创建存储过程的两种方式:

         1、直接手写;
         2、利用PL/SQL工具--》程序窗口
create or replace procedure pre1(str varchar2)
is
V_var varchar2(50);
begin
  V_var := str;
  dbms_output.put_line(V_var);
end;

SQL窗口执行

declare 
  V_a varchar2(50) :='&V_a';
begin
  pre1(V_a);
end;

命令窗口执行

exec pre1('hello world');

带参的存储过程

存储过程参数的传递方式:

按位置传递:
实参按顺序将值传给形参
----创建一个存储过程,用于向数据库中插入一条记录。
create or replace procedure insert_stu(
                  id number,
                  sname varchar2,
                  age number,
                  sex varchar2              
) AS
begin
   insert into student(stu_id,stu_name,stu_age,stu_sex)
   values(id,sname,age,sex);               
end;

执行

declare 
  v_id number:=100;
  v_name varchar2(30) :='哈哈';
  v_age number :=23;
  v_sex varchar2(10):='女';
 begin
      insert_stu (v_id,v_name,v_age,v_sex) ;
 end;

参数的传递:
①位置表示 ②名称表示法 ③混合表示法

declare 
  v_id number:=101;
  v_name varchar2(30) :='哈哈';
  v_age number :=23;
  v_sex varchar2(10):='女';
 begin
      insert_stu (id =>v_id,sname=>v_name,age =>v_age,sex=>v_sex) ;
 end;

在混合表示法中 位置表示法必须位于名称表示法之前

declare 
  v_id number:=102;
  v_name varchar2(30) :='哈哈';
  v_age number :=23;
  v_sex varchar2(10):='女';
 begin
      insert_stu (v_id,v_name,age =>v_age,sex=>v_sex) ;
 end;

当参数为输入参数时 IN可以省略
存储过程 携带输出参数

--根据部门编号 来统计部门的人数和工资综合
create or replace procedure count_emp(
       V_deptno IN number,
       V_num OUT number,
       V_sum OUT number

)
AS 
begin
  select count(*),sum(salary) into V_num,V_sum from emp where department_id = V_deptno;
end count_emp;

调用

declare
  V_no number := 90;
  V_num number;
  V_sum number;
begin
  count_emp(V_no,V_num,V_sum);
  dbms_output.put_line(V_num);
  dbms_output.put_line(V_sum);
end;

输入输出参数

--根据部门编号 来统计部门的人数和工资综合
create or replace procedure count_emp(
       V_no IN OUT number,
 
       V_sum OUT number

)
AS 
begin
  select count(*),sum(salary) into V_no,V_sum from emp where department_id = V_no;
end count_emp;
declare
  V_no number := 90;
  V_sum number;
begin
  count_emp(V_no,V_sum);
  dbms_output.put_line(V_no);
  dbms_output.put_line(V_sum);
end;

3.触发器

触发器在数据库里以独立的对象存储,它与存储过程不同的是,存储过程通过其它程序来启动运行或直接启动运行,而触发器是由一个事件来启动运行。即触发器是当某个事件发生时自动地隐式运行。并且,触发器不能接收参数。
ORACLE事件指的是对数据库的表进行的INSERT、UPDATE及DELETE操作或对视图进行类似的操作。

  1. DML触发器
    ORACLE可以在DML语句进行触发,可以在DML操作前或操作后进行触发,并且可以对每个行或语句操作上进行触发。
  2. 替代触发器
    由于在ORACLE里,不能直接对由两个以上的表建立的视图进行操作。所以给出了替代触发器。它就是ORACLE 8专门为进行视图操作的一种处理方法。
  3. 系统触发器
    ORACLE 8i 提供了第三种类型的触发器叫系统触发器。它可以在ORACLE数据库系统的事件中进行触发,如ORACLE系统的启动与关闭等
    触发器组成:
  4. 触发事件:即在何种情况下触发TRIGGER; 例如:INSERT, UPDATE, DELETE。
  5. 触发时间:即该TRIGGER 是在触发事件发生之前(BEFORE)还是之后(AFTER)触发,也就是触发事件和该TRIGGER 的操作顺序。
  6. 触发器本身:即该TRIGGER 被触发之后的目的和意图,正是触发器本身要做的事情。 例如:PL/SQL 块。
  7. 触发频率:说明触发器内定义的动作被执行的次数。即语句级(STATEMENT)触发器和行级(ROW)触发器
    语句级(STATEMENT)触发器:是指当某触发事件发生时,该触发器只执行一次;
    行级(ROW)触发器:是指当某触发事件发生时,对受到该操作影响的每一行数据,触发器都单独执行一次。

3.1.触发器的创建

创建触发器的一般语法是:

CREATE [OR REPLACE] TRIGGER trigger_name
{BEFORE | AFTER | INSTEAD OF}
{INSERT | DELETE | UPDATE [OF column [, column]]}
ON {[schema.] table_name | [schema.] view_name}
[REFERENCING {OLD [AS] old | NEW [AS] new| PARENT as parent}]
[FOR EACH ROW ]
[WHEN condition]
trigger_body;

BEFORE 和AFTER指出触发器的触发时序分别为前触发和后触发方式,前触发是在执行触发事件之前触发当前所创建的触发器,后触发是在执行触发事件之后触发当前所创建的触发器。
当一个基表被修改( INSERT, UPDATE, DELETE)时要执行的存储过程,执行时根据其所依附的基表改动而自动触发,因此与应用程序无关,用数据库触发器可以保证数据的一致性和完整性。
问题:当触发器被触发时,要使用被插入、更新或删除的记录中的值,有时要使用操作前、后的值.
实现:

:new  修饰符访问操作完成后列的值
:old  修饰符访问操作完成前列的值
特性 INSERT UPDATE DELETE
OLD NULL 有效 有效
NEW 有效 有效 NULL

3.2.触发器的运用

创建备份表

create table stu_bak as select * from student where 1=2;

创建触发器

--当删除student表中的数据的时候  将该记录保存到stu_bak备份表中
create or replace trigger tri_stu_bak
before delete --在执行删除之前触发
on student
for each row--行级触发器
  --触发器要完成的操作
declare

begin
  insert into stu_bak(stu_id,stu_name,stu_age,stu_sex,cls_id)
  values(:old.stu_id,:old.stu_name,:old.stu_age,:old.stu_sex,:old.cls_id);
end;

触发触发器

delete from student where stu_id = 6;

应用:

电商平台
触发器
仓储管理

3.3.删除触发器\函数\存储过程

删除触发器:

DROP TRIGGER trigger_name;

删除函数:

DROP function function_name;

删除存储过程:

DROP procedure procedure_name;

4.包

PL/SQL包是组逻辑相关的PL/SQL类型,变量和子程序模式对象。
程序包将有两个强制性的部分:

 包规范定义
 包体的定义 

4.1.包规范定义

规范是接口到包。它只是声明的类型,变量,常量,异常,游标和子程序可从封装外部引用。换句话说,它包含关于包的内容的所有信息,但不包括用于子程序的代码。
置于规范的所有对象被称为公共对象。任何子程序在封装主体中没有包定义但编码被称为私有对象。
下面的代码片段显示了具有单一的程序包规范定义。一个包中可以定义的全局变量和多个程序或函数。

create or replace package pack_emp
as
procedure count_sal(V_deptno emp.department_id%TYPE);
END pack_emp;

4.2.包主体

包体已经在包定义和其他私人声明中声明的各种方法,这是从代码隐藏在包外的代码。
CREATE PACKAGE BODY语句用于创建包体。

create or replace package body pack_emp as
procedure count_sal(V_deptno emp.department_id%TYPE)
IS
 v_num number;
          
BEGIN
  
 select count(*) into v_num from emp where department_id = V_deptno;
 dbms_output.put_line(v_num);
END count_sal;
end pack_emp;

调用:

declare 
V_no emp.department_id%Type :=90;
begin
  pack_emp.count_sal(V_no);
end;
发布了67 篇原创文章 · 获赞 6 · 访问量 1923

猜你喜欢

转载自blog.csdn.net/weixin_45801537/article/details/104408453
今日推荐