Oracle PL/SQL
2017-09-22 00:00
274 查看
一、.基本语法
PL/SQL的结构块如下:declare
--声明变量、类型、游标
begin
--程序执行的部分,类似main方法
exception
--针对异常,提供如何处理的机制
--when ....then....
end;
注意,没有用的部分,就 不需要写,比如程序的确是没有异常要处理,那么exception就不写
建议的命名方法:
标识符 命名规则 例子
程序变量 V_name V_name
程序常量 C_Name C_company_name
游标变量 Name_cursor Emp_cursor
异常标识 E_name E_too_many
表类型 Name_table_typeEmp_record_type
表 Name_table Emp
记录类型 Name_recordEmp_record
SQL*Plus替代变量 P_name P_sal
绑定变量 G_name G_year_sal
HelloWorld演示:
begin dbms_output.put_line('HelloWorld'); end;
输出100员工的工资:
declare v_sal number(20) :=0; --声明变量,要注意的是,数据类型和要查询的表中的数据类型是对应得 --附一个默认值,:= v_email varchar2(20);--另一种写法,v_email employees.email % type v_hire_date date; --v_hire_date employees.hire_date % type //v_hire_date与employees表中的hire_date类型一样 --%type ,如果自定义类型的精度不够,那么%type就是动态的获取表中的类型及精度 begin select salary,email , hire_date into v_sal,v_email,v_hire_date from employees where employee_id = 100; --实现select操作,注意是select into dbms_output.put_line(v_sal||','||v_email||','||v_hire_date);--打印 end;
修改数据
declare v_emp_id employees.employee_id%type; begin v_emp_id :=100; update employees set salary = salary + 100 where employee_id = v_emp_id; dbms_output.put_line('OK'); end;
使用%TYPE
定义一个变量,其数据类型与已经定义的某个 数据变量的类型相同,或者与数据库表的某个列的数据类型
相同,这时可以使用%TYPE。
使用%TYPE 特性的优点在于:
所引用的数据库列的数据类型可以不必知道;
所引用的数据库列的数据类型可以实时改变。
使用%ROWTYPE
PL/SQL 提供%ROWTYPE 操作符, 返回一个记录类型, 其数据类型和数据库表的数据结构相一致。
使用%ROWTYPE 特性的优点在于:
所引用的数据库中列的个数和数据类型可以不必知道;
所引用的数据库中列的个数和数据类型可以实时改变。
二、记录类型
type ... is record(,,);DECLARE --声明一个记录类型,类似于java中的类 TYPE emp_record IS RECORD( v_sal employees.salary % type, v_email employees.email % type,--以逗号链接 v_hire_date employees.hire_date % type);--分号 --定义一个记录变量 v_emp_record emp_record; BEGIN SELECT salary , email, hire_date INTO v_emp_record FROM employees WHERE employee_id = 101; dbms_output.put_line(v_emp_record.v_sal||','||v_emp_record.v_email||','||v_emp_record.v_hire_date); END;
获取一个表中所有列的记录类型
declare v_emp_record employees%rowtype; v_emp_id number(10); begin v_emp_id := 100; select * into v_emp_record from employees where employee_id = v_emp_id; dbms_output.put_line(v_emp_record.salary||' , '||v_emp_record.last_name);--用表中的列名字即可 end;
三、流程控制:
条件判断:(两种)方式一:if ..then elseif then...else...end if;
方式二:case...when...then ..end;
循环结构:(三种)
方式一:loop...exit when ...end loop;
方式二:while..loop...end;
方式三:for i in ..loop ..end loop;
关键字:goto (类似java中的break后接一个标签,跳出那个循环)、 exit
条件判断:
--查询出 150号 员工的工资, 若其工资大于或等于 10000 则打印 'salary >= 10000';
--若在 5000 到 10000 之间, 则打印 '5000<= salary < 10000'; 否则打印 'salary < 5000'
方式一
declare v_sal employees.salary%type; v_result varchar2(20); begin select salary into v_sal from employees where employee_id = 150; if v_sal >= 10000 then v_result := 'salary >= 10000'; elsif v_sal >=5000 then v_result := 'salary >= 5000'; else v_result := 'salary < 5000'; end if; dbms_output.put_line(v_sal||' , '||v_result); end;
方式二
declare v_sal employees.salary%type; v_result varchar2(20); begin select salary into v_sal from employees where employee_id = 150; v_result := case trunc(v_sal/5000) when 2 then 'salary >= 10000' when 1 then '5000 <= salary < 10000' when 0 then 'salary < 5000' end; dbms_output.put_line(v_sal||' , '||v_result); end;
case...when..then..相当于java的switch,使用起来有局限性,还是if..then..elsif...then比较好
注意,if..then每一次后面都要加“分号”,而case..when..then不能加,分号的问题在PL/DQL中比较坑
循环结构:
--使用循环语句打印 1 - 100.(三种方式)
--(1)初始化 (2)循环体 (3)循环条件 (4)迭代条件
declare --(1) v_i number(3) := 1; /*方式一 begin loop --(2) dbms_output.put_line(v_i); --(4) v_i := v_i + 1; --(3) exit when v_i >100; end loop;--注意每一行,都要有“分号” end;*/ 方式二:(推荐使用 ) begin while v_i <= 100 loop dbms_output.put_line(v_i); v_i := v_i + 1; end loop; end; /*方式三 begin for i in 1..100 loop --for i in reverse 1..100 loop,这样就是反着,从100-1,还要注意in后面是两个“.” dbms_output.put_line(i); end loop; end; */
输出1-100的所有质数
declare v_i number(3) := 2; v_j number(3) := 2; v_flag number(3) := 1; begin /*while v_i<=100 loop while v_j <= sqrt(v_i) loop if mod(v_i,v_j)=0 then v_flag := 0; goto OK; end if; v_j := v_j + 1; end loop; <<OK>> if v_flag = 1 then dbms_output.put_line(v_i); end if; v_j := 2; v_i := v_i + 1; v_flag := 1; end loop; */ for i in 2..100 loop for j in 2..sqrt(i) loop if mod(i,j)=0 then v_flag := 0; goto OK; end if; end loop; <<OK>> if v_flag = 1 then dbms_output.put_line(i); end if; v_flag := 1; end loop; end;
对于while 循环的嵌套。特别要注意初始化条件
四、游标的使用(类似于java的iterator)
主要方便用于处理多行数据。筛选出employees表中department_id为80的所有员工的salary,last_name.
declare --定义一个记录类型 type v_record is record( v_sal employees.salary%type, v_last_name employees.last_name%type ); --声明一个记录类型 v_emp_record v_record; --定义一个游标 cursor emp_sal_cursor is select salary,last_name from employees where department_id = 80; begin --打开游标 open emp_sal_cursor; --提取游标 fetch emp_sal_cursor into v_emp_record; while emp_sal_cursor%found loop dbms_output.put_line(v_emp_record.v_last_name||' : '||v_emp_record.v_sal); fetch emp_sal_cursor into v_emp_record; end loop; close emp_sal_cursor; end;
游标的使用,用for比较简单
declare cursor emp_sal_cursor is select salary,last_name from employees where department_id = 80; begin for c in emp_sal_cursor loop dbms_output.put_line(c.last_name||c.salary); end loop; end;
带参数的游标
declare --定义游标 cursor emp_sal_cursor(dept_id number, sal number) is select salary + 1000 sal, employee_id id from employees where department_id = dept_id and salary > sal; --定义基数变量 temp number(4, 2); begin --处理游标的循环操作 for c in emp_sal_cursor(sal => 4000, dept_id => 80) loop --判断员工的工资, 执行 update 操作 --dbms_output.put_line(c.id || ': ' || c.sal); if c.sal <= 5000 then temp := 0.05; elsif c.sal <= 10000 then temp := 0.03; elsif c.sal <= 15000 then temp := 0.02; else temp := 0.01; end if; dbms_output.put_line(c.sal || ': ' || c.id || ', ' || temp); --update employees set salary = salary * (1 + temp) where employee_id = c.id; end loop; end;
隐式游标
隐式游标属性
SQL%FOUND 布尔型属性,当最近一次读记录时成功返回,则值为 TRUE;
SQL%NOTFOUND 布尔型属性,与%FOUND 相反;
SQL %ROWCOUNT 数字型属性, 返回已从游标中读取得记录数;
SQL %ISOPEN 布尔型属性, 取值总是 FALSE。SQL 命令执行完毕立即关闭隐式游标。
--更新指定员工信息,如果该员工没有找到,则打印”查无此人”信息。
declare v_sal employees.salary%type; v_empid employees.employee_id%type := 101; begin update employees set last_name = 'ABC' where employee_id = v_empid; if sql%notfound then -- dbms_output.put_line('查无此人'); end if; end;
五、异常处理(三种类型)
异常处理概念异常情况处理(EXCEPTION) 是用来处理正常执行过程中未预料的事件, 程序块的异常处理预定义的错误
和 自定义错误, 由于 PL/SQL 程序块一旦产 生异常而没有指出如何处理时,程序就会自动终止整个程序运行
共有三种类型:
1.预定义 ( Predefined ) 错误
ORACLE 预定义的异常情况大约有 24 个。对这种异常情况的处理,无需在程序中定义,由 由 ORACLE 自动
将其引发。
2.非预定义 ( Predefined )错误
即其他标准的 ORACLE 错误。对这种异常情况的处理,需要用户在程序中定义,然后由 ORACLE 自动将
其引发。
3.用户定义(User_define) 错误
程序执行过程中,出现编程人员认为的非正常情况。对这种异常情况的处理, 需要 用户在程序中定义,
然后显式地在程序中将其引发。
异常处理的基本结构:
EXCEPTION WHEN first_exception THEN <code to handle first exception > WHEN second_exception THEN <code to handle second exception > WHEN OTHERS THEN <code to handle others exception > END;
异常处理可以按任意次序排列,但 OTHERS 必须放在最后.
预定义异常
declare v_sal employees.salary%type; begin select salary into v_sal from employees where employee_id > 100; dbms_output.put_line(v_sal); exception when too_many_rows then dbms_output.put_line('要输出的行数太多了!'); when others then dbms_output.put_line('其他错误!'); end;
非预定义异常
步骤如下:
1.在 PL/SQL 块的定义部分定义异常情况:
<异常情况> EXCEPTION;
2. 将其定义好的异常情况,与标准的 ORACLE 错误联系起来,使用 PRAGMA EXCEPTION_INIT 语句:
PRAGMA EXCEPTION_INIT(< 异常情况>, < 错误代码>);
3. 在 PL/SQL 块的异常情况处理部分对异常情况做出相应的处理。
SQL>delete employees where employee_id = 100 ORA-02292: 违反完整约束条件 (SCOTT.DEPT_MGR_FK) - 已找到子记录 因为自己表里有manager_id也指向自己表里的employee_id,而删除emp_id,因为manager_id指向,所以或无法删除 declare e_deleteid_exception exception; pragma exception_init(e_deleteid_exception,-2292);--将2292这个错和自己定义的异常名称关联起来 begin delete employees where employee_id = 100; exception when e_deleteid_exception then dbms_output.put_line('违反完整性约束异常!'); end;
用户自定义的异常处理
当与一个异常错误相关的错误出现时,就会隐含触发该异常错误。 用户定义的异常错误是通过显式使
用 RAISE 语句来触发。当引发一个异常错误时,控制就转向到 EXCEPTION 块异常错误部分,执行错误处
理代码。
步骤如下:
1. 在 在 PL/SQL 块的定义部分定义异常情况:
< 异常情况> EXCEPTION;
2. RAISE < 异常情况>;
3.在 PL/SQL 块的异常情况处理部分对异常情况做出相应的处理
在非预言定义异常的基础上
declare e_deleteid_exception exception; pragma exception_init(e_deleteid_exception,-2292); e_too_high_sal_exception exception; --自定义异常 v_sal employees.salary % type; begin delete from employees where employee_id = 100; select salary into v_sal from employees where employee_id = 100; if v_sal > 10000 then raise e_too_high_sal_exception; --抛出异常 end if; exception when e_too_high_sal_exception then dbms_output.put_line('工资太高了'); when e_deleteid_exception then dbms_output.put_line('违反完整性约束异常'); when others then dbms_output.put_line('其他异常'); end;
1-5为基础中的基础
六、存储函数和存储过程
过程和函数的唯一区别是函数总向调用者返回数据,而过程则不返回数据。即存储函数(有返回值)、存储过程(无返回值)
-----存储函数:
创建函数 1. 建立内嵌函数 语法如下: 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 包含返回结果的数据类型。
--存储函数结构
create or replase function func_name (depart_id number, salary number) return number --返回某部门所有的工资 is --函数在使用过程中,需要声明的变量、记录类型record、游标cursor begin --函数的执行体 exception --处理函数执行过程中的异常 end;
Hello,World演示
create or replace function HelloDemo return varchar2 is begin return 'Hello,World'; end;
显示Function created,说明函数已经创建
调用函数
方式一:
begin dbms_output.put_line(HelloDemo); end;
方式二:
SQL>select HelloDemo from dual; /* create or replace function HelloDemo(v_world varchar2) return varchar2 is begin return 'Hello'||v_world; end; *//* select HelloDemo('Worrld') from dual */
获取系统时间函数
create or replace function get_sysdate return date is v_date date; begin v_date := sysdate; return v_date; end; select get_date from dual;
定义两个数相加的函数
create or replace function add_func(v_num1 number,v_num2 number) return number is v_sum number(10);--这里需要指定长度 begin v_sum := v_num1 + v_num2; return v_sum; end; select add_func(1,2) from dual;
获取指定部门的工资总和
create or replace function get_all_sal(dept_id number) return number is v_sumsal number(20,2) := 0; cursor salary_cursor is select salary from employees where department_id = dept_id; begin for c in salary_cursor loop v_sumsal := v_sumsal + c.salary; end loop; return v_sumsal; end;
关于OUT形的参数
因为函数只能有一个返回值,PL/SQL程序可以通过OUT参数实现多个返回值
获取指定部门的工资总和,人员总和
create or replace function get_all_sal(dept_id number,emp_num out number)--注意out这里 return number is v_sumsal number(20,2) := 0; cursor salary_cursor is select salary from employees where department_id = dept_id; begin emp_num := 0; for c in salary_cursor loop v_sumsal := v_sumsal + c.salary; emp_num := emp_num + 1; end loop; return v_sumsal; end; 输出 declare v_num number(3):=0; begin dbms_output.put_line(get_all_sal(80,v_num)); dbms_output.put_line(v_num); --v_num就是部门人数 end;
-----存储过程:
--定义存储过程,实现指定部门的工资(通过out参数),要求部门id和工资总数为参数
create or replace procedure get_sal(dept_id number,v_sal out number)--不需要返回值 is cursor salary_cursor is select salary from employees where department_id = dept_id; begin v_sal := 0; for c in salary_cursor loop v_sal := v_sal + c.salary; end loop; dbms_output.put_line(v_sal); end;
因为存储过程不需要返回值,所以可以把增删改的操作定义存储过程
七、触发器
触发器是许多关系数据库系统都提供的一项技术。在 ORACLE 系统里, 触发器类似过程和函数,都有声明,执行和异常处理过程的 PL/SQL
语法:
CREATE [OR REPLACE] TRIGGER trigger_name {BEFORE | AFTER } {INSERT | DELETE | UPDATE [OF column [, column …]]} [OR {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] PL/SQL_BLOCK | CALL procedure_name;
其中:
BEFORE 和AFTER指出触发器的触发时序分别为前触发和后触发方式,前触发是在执行触发事件之前触发当前所创建的触发器,后触发是在执行触发事件之后触发当前所创建的触发器。
FOR EACH ROW选项说明触发器为行触发器。行触发器和语句触发器的区别表现在:行触发器要求当一个DML语句操走影响数据库中的多行数据时,对于其中的每个数据行,只要它们符合触发约束条件,均激活一次触发器;而语句触发器将整个语句操作作为触发事件,当它符合约束条件时,激活一次触发器。当省略FOR EACH ROW 选项时,BEFORE 和AFTER 触发器为语句触发器,而INSTEAD OF 触发器则只能为行触发器。
REFERENCING 子句说明相关名称,在行触发器的PL/SQL块和WHEN 子句中可以使用相关名称参照当前的新、旧列值,默认的相关名称分别为OLD和NEW。触发器的PL/SQL块中应用相关名称时,必须在它们之前加冒号(:),但在WHEN子句中则不能加冒号。
WHEN 子句说明触发约束条件。Condition 为一个逻辑表达时,其中必须包含相关名称,而不能包含查询语句,也不能调用PL/SQL 函数。WHEN 子句指定的触发约束条件只能用在BEFORE 和AFTER 行触发器中,不能用在INSTEAD OF 行触发器和其它类型的触发器中。
当一个基表被修改( INSERT, UPDATE, DELETE)时要执行的存储过程,执行时根据其所依附的基表改动而自动触发,因此与应用程序无关,用数据库触发器可以保证数据的一致性和完整性。
一个helloworld级别的触发器
create or replace trigger hello_trigger after update on employees --for each row begin dbms_output.put_line('hello...'); --dbms_output.put_line('old.salary:'|| :OLD.salary||',new.salary'||:NEW.salary); end;
然后执行:update employees set salary = salary + 1000;
DML触发器基本要点
l 触发时机:指定触发器的触发时间。如果指定为BEFORE,则表示在执行DML操作之前触发,以便防止某些错误操作发生或实现某些业务规则;如果指定为AFTER,则表示在执行DML操作之后触发,以便记录该操作或做某些事后处理。
l 触发事件:引起触发器被触发的事件,即DML操作(INSERT、UPDATE、DELETE)。既可以是单个触发事件,也可以是多个触发事件的组合(只能使用OR逻辑组合,不能使用AND逻辑组合)。
l 条件谓词:当在触发器中包含多个触发事件(INSERT、UPDATE、DELETE)的组合时,为了分别针对不同的事件进行不同的处理,需要使用ORACLE提供的如下条件谓词。
1)。INSERTING:当触发事件是INSERT时,取值为TRUE,否则为FALSE。
2)。UPDATING [(column_1,column_2,…,column_x)]:当触发事件是UPDATE 时,如果修改了column_x列,则取值为TRUE,否则为FALSE。其中column_x是可选的。
3)。DELETING:当触发事件是DELETE时,则取值为TRUE,否则为FALSE。
解发对象:指定触发器是创建在哪个表、视图上。
l 触发类型:是语句级还是行级触发器。
l 触发条件:由WHEN子句指定一个逻辑表达式,只允许在行级触发器上指定触发条件,指定UPDATING后面的列的列表。
问题:当触发器被触发时,要使用被插入、更新或删除的记录中的列值,有时要使用操作前、 后列的值.
实现: :NEW 修饰符访问操作完成后列的值
:OLD 修饰符访问操作完成前列的值
特性 | INSERT | UPDATE | DELETE |
OLD | NULL | 实际值 | 实际值 |
NEW | 实际值 | 实际值 | NULL |
相关文章推荐
- Oracle 11g Release 1 (11.1) 游标——在 PL/SQL 管理游标
- Oracle PL/SQL Developer使用技巧
- PL/SQL Developer中调试oracle的存储过程
- 免安装Oracle客户端使用PL/SQL连接Oracle
- [推荐]ORACLE PL/SQL编程之四:把游标说透(不怕做不到,只怕想不到)
- pl/sql 本机不安装oracle服务端连接服务器
- Oracle PL/SQL处理CLOB字段的经验
- 免安装Oracle客户端使用PL/SQL连接SQL
- oracle pl sql import export
- Oracle PL/SQL之EXCEPTION -- WHEN OTHERS THEN
- Oracle PL/SQL之处理index不连续的table类型变量
- Oracle PL/SQL之LOOP循环控制语句
- PL/SQL Developer 使用oracle_client 连接虚拟机oracle的一些问题
- 用Oracle PL/SQL 编程实现小数转分数的方法
- Data Types with Different Maximum Sizes in oracle PL/SQL and SQL
- 使用instantclient_11_2 和PL/SQL Developer工具包连接oracle 11g远程数据库
- Oracle PL/SQL 编程手册(SQL大全)(转)
- [顶]ORACLE PL/SQL编程详解之二:PL/SQL块结构和组成元素(为山九仞,岂一日之功)
- Oracle PL/SQL基础语法 ——Oracle Database 11g R2
- oracle pl/sql入门之案例实践