Oracle常用命令08(pl

    技术2022-05-20  43

    【期望目标】 ①理解oracle的pl/sql概念 ②掌握pl/sql编程技术(包括编写存储过程、函数,触发器,包...)

    【pl/sql是什么】 pl/sql(procedural language/sql)是oracle在标准的sql语言上的扩展。pl/sql不仅允许 嵌入sql语言,还可以定义[变量]和[常量],允许使用[条件语句]和[循环语句],允许[使用 例外处理各种错误],这样使得他的功能变得更加强大。 ①过程、函数、触发器是pl/sql编写的 ②过程、函数、触发器实在oracle中的【减少对项目的成本:对复杂的一块业务,由专人负责】  【分页的过程:表名,第几页,每页几条记录】  【订单的过程】  【转账的过程】 ③pl/sql是非常强大的数据库过程语言 ④过程,函数可以在java程序中调用

    【pl/sql学习的必要性】 ①提高应用程序的运行性能【一个程序员的成长是需要过程的,把程序做的有灵魂】  传统的操作数据库的方法是:java程序运行的机器不一定和数据库在一起,即使在一起  他也有这样一个过程:得到一个数据库的连接(不管是用jdbc还是连接池的方式),首先  要传递给数据库sql语句,数据库拿到sql语句之后首先要进行编译(以形成数据库能  识别的代码),然后才会执行并查询客户端所请求的数据。编译究竟花多少时间?一次  编译花的时间并不多,但是每次sql语句来了之后都要编译的话,那花费的代价就很大  了...为什么用pl/sql编写过程、函数、触发器以后他的速度就会提高呢?客户端发过来  的sql语句就能尽可能地少!对于一个过程来说,他已经提前被编译好了。客户端的java  程序调用服务器端的过程时(过程已经被编译为能够直接运行的代码,因此就少了编译  的过程),只需要写上过程名再传递一些必要的参数就完事儿了。而且,我们可以轻一些  高人,技术强的人来专门写这个过程,比如订单处理模块的存储过程,分页的,转账的  存储过程,专门维护他就行了。为什么oracle程序员一定要求会pl/sql的道理,他对写  过程、写函数的要求是很高的,而且确实能够提高整系统的性能。如果系统跑得慢,可以  有以下几种方法解决:1.如果是传统方法的话,那就优化sql语句;2.如果优化sql语句性能  提升有限的话,还可以把sql语句做成模块写成一个个的存储过程,然后调用存储过程就  行了。  ②模块化的设计思想 ③减少网络传输量

    【pl/sql不好的地方】 移植性不好。如果换了数据库的话,存储过程就要全部重写! 如果是用hibernate的hql语句的话,数据库就可以算便换~ 一般在哪种情况下才换数据库?项目用这个数据库实在是不能跑了才会换,客户不会关心  后台用什么数据库的!

    【用什么编写 pl/sql】 ① sqlplus 开发工具 sqlplus 是 oracle 公司提供的一个工具,这个因为我们在以前介绍过的: 举一个简单案例:编写一个存储过程,该过程可以向某表中添加记录。

    1.创建一张简单的表  create table mytest(name varchar2(30),password varchar2(30));

    2.创建存储过程【bruce_pro1 是过程名,不能有两个过程同名】 -------------------------------------------------------- create [or replace] procedure bruce_pro1 is begin --执行部分 insert into mytest('杨长伟','123abc!'); end; -------------------------------------------------------- 【回车过后你会看到它仍然没有执行,加一个/, /就代表让oracle去创建这个存储过程,之后在回车】 【[or replace]:如果有的话,bruce_pro1 就替换,没有就新建~】 【如报错“名称已由现有对象使用”:改名即可】 【报错:“创建的过程带有编译错误”,修改存储过程为如下】 -------------------------------------------------------- create or replace procedure bruce_pro1 is begin --执行部分 insert into mytest values('杨长伟','123abc!'); end; -------------------------------------------------------- 【如何查看错误信息:show error; 即可】 【如返回“过程已创建”,则表示创建成功】 -------------------------------------------------------- SQL> create or replace procedure bruce_pro1 is   2  begin   3  --执行部分   4  insert into mytest values('杨长伟','123abc!');   5  end;   6  /   Procedure created --------------------------------------------------------

    【如何调用该过程?】 1.exec 过程名(参数值1, 参数值2...); 2.call 过程名(参数值1, 参数值2...);

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

    ②pl/sql developer开发工具 【SQL Server 2000也可以进行pl/sql编程,不过关于这个业界没有标准】 pl/sql developer是用于开发pl/sql块地集成开发环境(ide),他是一个独立的产品,而不是 oracle的一个附带品。 举一个简单案例:编写一个存储过程,该过程可以删除某表记录。 -------------------------------------------------------- create or replace procedure bruce_pro2 is begin --执行部分 delete from mytest where name='杨长伟'; end; --------------------------------------------------------

    【pl/sql基础介绍】 开发人员使用pl/sql编写应用模块时,不仅需要掌握sql语句的编写方法,还要掌握 pl/sql语句及语法规则。pl/sql编程可以使用变量和逻辑控制语句,从而可以编写非 常有用的功能模块。比如:分页存储过程模块、订单处理存储过程模块、转账存储过 程模块..而且如果使用pl/sql编程,我们可以轻松的完成非常复杂的查询要求。

    【简单分类(基本上单位的要求也就这些,足够了)】   |-----过程(存储过程)     |   |-----函数 块(编程)--------|   |-----触发器   |   |-----包

    【编写规范】 ①注释 单行注释 -- select * from emp where empno=7788;--取得员工信息 多行注释 /*...*/ 来划分

    ②表示符号的命名规范 1.当定义变量时,建议用 v_ 作为前缀 v_sal 2.当定义常量时,建议用 c_ 作为前缀 c_rate 3.当定义游标时,建议用 _cursor 作为后缀 emp_cursor; 4.当定义例外时,建议用 e_ 作为前缀 e_error

    【pl/sql块的介绍】 块(block)是pl/sql的基本程序单元,编写pl/sql程序实际上就是编写pl/sql块。 要完成相对简单的应用功能,可能只需要编写一个pl/sql块;但是如果要想实现 复杂的功能,可能需要在一个pl/sql块中嵌套其他的pl/sql块。

    【块的结构示意图】 pl/sql块由三个部分构成:定义部分、执行部分、例外处理部分。 如下所示: declare /*定义部分-----定义常量、变量、游标、例外、复杂数据类型*/ begin /*执行部分-----要执行的pl/sql语句和sql语句*/ exception /*例外处理部分-----处理运行的各种错误*/ end;

    定义部分是从declare开始的,该部分是可选的。 执行部分是从begin开始的,该部分是必须的。 例外处理部分是从exception开始的,该部分是可选的。 【可以和java编程做一个简单的比较】

    【关于抛出异常】 java程序结果 public static void main(String[] args) {  int a = 1;  try {   a++;   // System.out 是java中的包   System.out.println("fasdfa");  } catch(Exception e) {   // 得到异常  } }

    【pl/sql块的实例】 【实例1-只包括执行部分的pl/sql块】

    set serveroutput on -- 打开输出选项 begin  dbms_output.put_line('hello'); end;

    相关说明:dbms_output是oracle所提供的包(类似java的开发包),该包包含 一些过程,put_line就是dbms_output包的一个过程。

     

    【实例2-包含定义部分和执行部分的pl/sql块】

    declare  v_ename varchar2(5); -- 定义字符串变量 begin  select ename into v_ename from emp where empno=&no;  dbms_output.put_line('雇员名:'||v_ename); end; /

    相关说明: ①& 表示要接收从控制台输入的变量, ②|| 表示 pl/sql 语句的连接符(有点儿类似于java中的字符串拼接) ③&no 是pl/sql developer 所特有的,作用是弹出一个对话框让输入参数 ④pl/sql编程中字符串是用单引号括起来的,用双引号的话则会报错。 ⑤& 表示要接收从控制台输入的变量

    【实例3-在实例2的基础上把雇员的薪水也显示出来】 最直接的想法: declare  v_ename varchar2(5);  v_sal number(7,2); begin  select ename into v_ename from emp where empno=&aa;  select sal into v_sal from emp where empno=&aa;  dbms_output.put_line('雇员名是:'||v_ename||' 薪水是:'||v_sal); end; /

    优化【ename,sal对应的顺序不能错】: declare  v_ename varchar2(5);  v_sal number(7,2); begin  select ename,sal into v_ename,v_sal from emp where empno=&aa;  dbms_output.put_line('测试异常是否终止程序执行!');  dbms_output.put_line('雇员名是:'||v_ename||' 薪水是:'||v_sal); end; /

    【实例3-包含定义部分、执行部分和例外处理部分】 为了避免pl/sql程序的运行错误,提高pl/sql的健壮性,应该对可能的错误进行处理, 这个很有必要: ①比如在实例2中,如果输入了不存在的雇员号,应当要做例外处理。 declare  v_ename varchar2(5);  v_sal number(7,2); begin  select ename,sal into v_ename,v_sal from emp where empno=&aa;  dbms_output.put_line('雇员名是:'||v_ename||' 薪水是:'||v_sal); exception  when no_data_found then  dbms_output.put_line('朋友,您的编号输入有误!'); end; /

    ②有时出现异常,希望用另外的逻辑处理【有了异常不捕获的程序就会终止不继续往下执行】 相关说明:oracle实现预定义了一些例外,no_data_found就是找不大数据的例外

    【过程】 过程用于执行特定的操作。当建立过程时,既可以指定输入参数(in),也可以指定输出 参数(out)。通过在过程中使用输入参数,可以将数据传递到执行部分;通过使用输出参数 ,可以将执行部分的数据传递到应用环境。在sqlplus中可以使用create procedure命令 来建立过程。 实例如下: ①请考虑编写一个过程,可以输入雇员名,新工资,可修改雇员的工资 ②如何调用过程有两种方法: ③如何在java程序中调用一个存储过程  try {   // 1.加载驱动   Class.forName("oracle.jdbc.driver.OracleDriver");   // 2.得到连接   Connection ct=DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:MYORA1","SCOTT","m123");      // 3.创建 CallableStatement   CallableStatement cs = ct.prepareCall("{call bruce_pro3(?, ?)}");      // 4.给?赋值   cs.setString(1, "SMITH");   cs.setInt(2, 10); // 把 SMITH 的工资降为 321      // 5.执行   cs.execute();      // 6.关闭连接   cs.close();   ct.close();  } catch (Exception e) {   e.printStackTrace();  } 特别说明:

    【案例4-接收输入参数,参数只需指明类型,不需指定大小,如number(3,2)不必要】

    create procedure bruce_pro3(name varchar2, newSal number) is begin --执行部分,根据用户名去修改工资  update emp set sal=newSal where ename=name; end; / 【如何调用该存储过程:call bruce_pro3('SCOTT',3456)、exec ...】 【存储过程主要还是给java程序去调用的,在pl/sql developer中执行顶多是测试~ 】 【每一种语言,比如java、C、C++、delphi,都是可以调用存储过程的~ 】

    【函数】 函数用于返回特定的数据,当监理函数时,在函数头部必须包含return子句,而在函数体内 必须包含return语句返回的数据。我们还可以使用create function 来建立函数,实际案例: 【年薪本身是数字类型的,所以返回number】 【number(7,2)表名具体的类型是什么】

    --函数案例 --输入雇员的姓名,返回该雇员的年薪 create or replace function bruce_fun1(spName varchar2) return number is yearSal number(7,2); begin   --执行部分   select sal*12+nvl(comm,0)*12 into yearSal from emp where ename=spName;   return yearSal; end; / 【竟然会有错误,如果想知道错误时什么的话,老规矩,show error;】

    【如何调用函数】 在sqlplus中调用函数 var yearSal number call bruce_fun1('SCOTT') into:yearSal; print yearSal;

    同样我们可以在 java 程序中调用该函数 select annual_income('SCOTT') from dual; // 这样 可以通过rs.getInt(1)得到返回的结果

    【包-和java的包其实还是很相似的(做复杂模块的时候)】 包用于在逻辑上组合过程和函数,他由包规范和包体两部分组成 ①我们可以使用create package 命令来创建包【相当于一个函数有了但是没有实现】: 实例:

    --创建包 --创建一个包 bruce_package --声明该包有一个过程 bruce_pro3 --声明该包有一个函数 bruce_fun1 create or replace package bruce_package is  procedure bruce_pro3(name varchar2, newSal number);  function bruce_fun1(spName varchar2) return number; end; /

    ----------------------------------------------------------- SQL> --创建包 SQL> --创建一个包 bruce_package SQL> --声明该包有一个过程 bruce_pro3 SQL> --声明该包有一个函数 bruce_fun1 SQL> create package bruce_package is   2   procedure bruce_pro3(name varchar2, newSal number);   3   function bruce_fun1(spName varchar2) return number;   4  end;   5  /   Package created -----------------------------------------------------------

    包的规范值包含了过程和函数的说明,但是没有过程和函数的实现代码。 包体用于实现包规范中的过程和函数。 ②建立包体可以使用 create package body 命令。 create package body bruce_package is  --过程的具体实现  procedure bruce_pro3(name varchar2, newSal number) is  begin   update emp set sal=newSal where ename=name;  end;  --函数的具体实现  function bruce_fun1(spName varchar2)  return number is yearSal number(7,2);  begin   select sal*12+nvl(comm,0) into yearSal from emp where ename=spName;   return yearSal;  end; end; / ----------------------------------------------------------- SQL> create package body bruce_package is   2   --过程的具体实现   3   procedure bruce_pro3(name varchar2, newSal number) is   4   begin   5    update emp set sal=newSal where ename=name;   6   end;   7   --函数的具体实现   8   function bruce_fun1(spName varchar2)   9   return number is yearSal number(7,2);  10   begin  11    select sal*12+nvl(comm,0) into yearSal from emp where ename=spName;  12    return yearSal;  13   end;  14  end;  15  /   Package body created   -----------------------------------------------------------

    【如何调用包的过程或是函数】 当调用包的过程或是函数时,在过程和函数前需要带上包名,如果要访问替他方案的包, 还需要在包名前加上方案名。 如:  call bruce_package.bruce_pro3('SCOTT', 1500);  exec bruce_package.bruce_pro3('SCOTT', 3000); 特别说明: 包是pl/sql中非常重要的部分,我们在使用过程分页时,将会再次体验到他的威力。

    【触发器】 触发器是指隐含的执行的存储过程。当定义触发器时,必须要指定出发的事件和出发的操作, 常用的出发事件包括insert,update,delete语句,而出发操作时机就是一个pl/sql块。可以 使用create trigger 来建立触发器。 特别说明: 我们会在后面详细为大家介绍触发器的使用,因为触发器是非常有用的,可以维护数据库的 安全和一致性。

    【定义并使用变量】 在编写pl/sql程序时,可以定义变量和常量;在pl/sql程序中包括有: ①标量类型(scalar) ②复合类型(composite) ③参照类型(reference) ④lob(large object)

    【标量(scalar)-常用类型】 在编写pl/sql块时,如果要使用变量,需在定义部分定义变量。 pl/sql中定义标量和常量的语法如下: identifier [constant] datatype [not null] [:=| default expr] identifier: 名称 constant:指定常量。需要指定他的初始值,且其值是不能改变的 datatype:数据类型 not null:指定变量值不能为null := 给变量或是常量指定初始值 default:用于指定初始值 expr:指定初始值的pl/sql表达式,可是文本值、其他变量、函数等

    【标量定义的案例】 ①定义个一个边长字符串:  v_ename varchar2(10); ②定义一个小数(范围 -9999.99~9999.99)  v_sal number(6,2) ③定义一个小数并给一个初始值为5.4:=是pl/sql的赋值号  v_sal number(6.2):=5.4 ④定义一个日期类型的数据  v_hiredate date; ⑤定义一个布尔变量,不能为空,初始值为false  v_valid boolean not null default false;

    【标量(scalar)- 使用标量】 【标量说白了就是存储最普通、最简单的变量,仅仅这个是不够用的~】 在定义号变量后,就可以使用这些变量。这里需要说明的是pl/sql块为 变量赋值不同于其他的编程语言,需要在等号前面加冒号(:=)

    下面以输入员工号,显示雇员姓名、工资、个人所得税(税率为0.03)。 说明变量的使用,看看如何编写。【number(3,2)表示有3位,2位小数】

    declare --定义其间要使用到的变量和常量  c_tax_rate number(3,2):=0.03;  --用户名  v_ename varchar2(5);--这个地方有漏洞,如果查到的姓名超过5个字符,则溢出  v_sal number(7,2);  v_tax_sal number(7,2); begin --执行  select ename,sal into v_ename,v_sal from emp where empno=&no;  --在pl/sql中完全可以用加减乘除来进行计算  --计算所得税  v_tax_sal:=v_sal*c_tax_rate;  --输出一下  dbms_output.put_line('姓名是'||v_ename||'工资:'||v_sal||'交税:'||v_tax_sal); end; /

    【标量(scalar)- 使用%type类型】 对于上面的pl/sql块有一个问题: 就是如果员工的姓名超过了5字符的话,就会有错误,为了降低pl/sql程序的维护工作, 可以使用%type属性定义变量,这样他会按照数据库列来确定你定义的变量的类型和长度 用法如下:  标示符名 表名.列名%type 上个案例的改进版【解决“character string buffer too small” 的问题】:

    declare --定义其间要使用到的变量和常量  c_tax_rate number(3,2):=0.03;  --用户名  v_ename emp.ename%type;--这个地方有漏洞,如果查到的姓名超过5个字符,则溢出  v_sal emp.sal%type;  v_tax_sal number(7,2); begin --执行  select ename,sal into v_ename,v_sal from emp where empno=&no;  --在pl/sql中完全可以用加减乘除来进行计算  --计算所得税  v_tax_sal:=v_sal*c_tax_rate;  --输出一下  dbms_output.put_line('姓名是'||v_ename||'工资:'||v_sal||'交税:'||v_tax_sal); end; /

    【复合变量(composite)- 介绍】 用于存放多个值的变量。主要包括这几种: ①pl/sql记录 ②pl/sql表 ③嵌套表 ④varray

    【复合类型-pl/sql记录】 类似于高级语言中的结构体,需要注意的是,当引用pl/sql记录成员时, 必须要加记录变量作为前缀(记录变量。记录成员)如下:

    --pl/sql记录实例 declare  --定义了一个 pl/sql 记录类型 emp_record_type,类型包含三个数据name,sal,job  type emp_record_type is record(   name emp.ename%type,   salary emp.sal%type,   title emp.job%type  );  --定义了一个 sp_record 变量,这个变量的类型是 emp_record_type  sp_record emp_record_type; begin  select ename,sal,job into sp_record from emp where empno=7788;  dbms_output.put_line('员工:'||sp_record.name||sp_record.salary||sp_record.title); end; / --------------------------------------------------------------- SQL> --pl/sql记录实例 SQL> declare   2   --定义了一个 pl/sql 记录类型 emp_record_type,类型包含三个数据name,sal,job   3   type emp_record_type is record(   4    name emp.ename%type,   5    salary emp.sal%type,   6    title emp.job%type   7   );   8   --定义了一个 sp_record 变量,这个变量的类型是 emp_record_type   9   sp_record emp_record_type;  10  begin  11   select ename,sal,job into sp_record from emp where empno=7788;  12   dbms_output.put_line('员工:'||sp_record.name||sp_record.salary||sp_record.title);  13  end;  14  /   员工:SCOTT3000ANALYST   PL/SQL procedure successfully completed ---------------------------------------------------------------

    【复合类型-pl/sql表 - 相当于高级语言中的数组】 【如果把where条件去掉会怎么样呢?】 但是需要注意的是在高级语言中数组的下表不能为负数, 而pl/sql是可以为负数的,并且表元素的下标没有限制。实例如下:

    --pl/sql表实例 declare  -- 定义了一个 pl/sql 表类型 sp_table_type,该类型用于存放 emp.ename%type 的数组  -- index by binary_integer 表示下表是整数  type sp_table_type is table of emp.ename%type index by binary_integer;  -- 定义了一个 sp_table 变量,这个变量的类型是 sp_table_type  sp_table sp_table_type; begin  -- 爱怎么放怎么放,table(1000)、table(-1) 也行!  -- 如果返回来的是一堆数据的话肯定会爆  select ename into sp_table(100) from emp where empno=7788;  dbms_output.put_line('员工名:'|| sp_table(100)); end; /

    说明: sp_table_type 是pl/sql表类型 emp.ename%type 指定了表的元素的类型和长度 sp_table 为pl/sql表变量 sp_table(0) 则表示下标为 0 的元素

    【怎么解决下面的问题?--应该使用参照变量!】 declare  -- 定义了一个 pl/sql 表类型 sp_table_type,该类型用于存放 emp.ename%type 的数组  -- index by binary_integer 表示下表是整数  type sp_table_type is table of emp.ename%type index by binary_integer;  -- 定义了一个 sp_table 变量,这个变量的类型是 sp_table_type  sp_table sp_table_type; begin  -- 爱怎么放怎么放,table(1000)、table(-1) 也行!  -- 如果返回来的是一堆数据的话肯定会爆  select ename into sp_table(100) from emp;  dbms_output.put_line('员工名:'|| sp_table(100)); end; /

    【参照变量-介绍】 参照变量是指用于存放数值指针的变量。通过使用参照变量。 可以使得应用程序共享相同对象,从而降低占用的空间。在编写 pl/sql程序时,可以使用【游标变量(ref cursor)】和对象类型变量 (ref obj_type) 两种参照变量类型。

    【参照变量-ref cursor游标变量,使用非常广泛、普遍】 使用游标时,当定义游标时不需要指定相应的select语句,但是当使用 游标时(open时)需要指定select语句,这样一个游标就与一个select语句 结合了。实例如下: ①请使用pl/sql编写一个块,可以输入部门号,并显示该部门所有员工姓名和他的工资。

    declare  --定义游标  type sp_emp_cursor is ref cursor;  --定义一个游标变量  test_cursor sp_emp_cursor;  --定义变量  v_ename emp.ename%type;  v_sal emp.sal%type; begin  --执行  --把test_cursor 和一个 select 结合  --把test_cursor 游标指向结果集  open test_cursor for select ename,sal from emp where deptno=&no;  --循环取出  loop   fetch test_cursor into v_ename,v_sal;   --判断工资高低,决定是否更新      --判断是否 test_cursor 为空(如果没有这句的话,就是死循环)   exit when test_cursor%notfound;   dbms_output.put_line('名字:'||v_ename||' 工资:'||v_sal);  end loop;  --关闭游标  close test_cursor; end; /  

    ②在①的基础上,如果某个员工的工资低于200员,就增加100员。


    最新回复(0)