PL/SQL编程基础

2019-05-11

PL(Procedural Language)/SQL概述

​ 在实际工作中,有些复杂的数据处理操作需要在客户端编程实现,但是客户端所处理的数据必须由数据库服务器通过网络传输而来,如果数据处理量非常大,采用客户端编程的方法就降低了数据处理速度。为了减少客户端与数据库服务器端之间的网络数据传输量,提高数据处理速度,可以直接在数据库服务器端编程,实现对数据的处理,处理完成后直接将处理结果返回客户端。为此Oracle提供了在数据库服务器端编程的语言,即PL/SQL。
​ SQL只是一种声明式语言,是非过程性的,语句之间相互独立。在实际工作中,许多事务处理应用都是过程性的,前后语句之间存在一定关联。PL/SQL是过程化SQL,可以弥补SQL语句的不足。可以通过IF和LOOP语句控制程序的执行流程;可以定义变量以便在语句之间传递数据信息。PL/SQL是对标准SQL语言的扩展, SQL语句完全可以嵌套在PL/SQL程序代码中,将SQL的数据处理能力和PL/SQL的过程处理能力结合在一起。

pl/sql的分类:

  • 匿名的pl/sql:只是一段pl/sql语句块,只是在运行时执行一次,不会在数据库系统中永久存储。

  • 命名的pl/sql:pl/sql存储在数据库系统中,可以按照名称重复调用。包括存储过程、函数、触发器等

[ declare 定义语句段 ; ]    --声明部分,可选项,用于声明变量
begin
    执行语句段 ;              --执行部分,可包含流程控制和sql语句
[ exception 异常处理语句段 ; ]     --异常处理部分,可选项
end;

示例:根据用户输入的员工编号获得其工资和奖金

declare
    var_empno number;  --员工编号
    var_sal number;    --工资
    var_comm number;   --奖金
begin
    --接收用户输入的员工编号
    var_empno:=&var_empno;
    --获得工资
    select sal,nvl(comm,0) into var_sal,var_comm from scott.emp where empno=var_empno;
    dbms_output.put_line('员工编号是'||var_empno||'的工资是'||var_sal||'奖金是'||var_comm);
end; 

示例:根据用户输入的员工编号进行涨工资10%,但是如果增长后的工资超过3200则不予增长

declare
    --员工编号
    var_empno number;
    --工资
    var_sal number;
begin
    --获得员工编号
    var_empno:=&var_empno;
    --根据编号获得其工资
    select sal into var_sal from scott.emp where empno=var_empno;
    --判断工资增长后是否超过3200
    if var_sal*1.1<=3200 then
        --更新工资
        update scott.emp set sal=sal*1.1 where empno=var_empno;
        commit;
    end if;
end; 

变量与数据类型

变量名 数据类型 := 初值 ; 
变量名 数据类型  default  初值 ;
  • 长度不能超过30个字符,不能有空格。
  • 由字母、数字、下划线、美元符号$和#号组成,必须以字母开头。
  • 不能使用PL/SQL或SQL的关键字。可以使用help reserved words命令获得关键字列表。
  • 变量默认值为null。

变量命名规范

命名规则
程序变量 v_name v_student_name
程序常量 c_name c_company_name
游标变量 cursor_name cursor_emp
异常标志 e_name e_too_many
表类型 name_table_type emp_record_type
name_table emp_table
记录类型 name_record emp_record
绑定变量 g_name g_year_sal

变量的标准数据类型:

 number:数字类型,可以代表整数和浮点数。

 int:整数型。

 pls_integer:整数型,计算速度快(直接由cpu执行),占用较少的存储空间。但产生溢出时将触发异常。

 binary_integer:带符号的整数型,不会出现溢出,但计算速度较慢(由oracle模拟执行)。

 char:定长字符,最长255个字符。

 varchar2:变长字符,最长2000个字符。

 long:变长字符,最长2gb。

 date:日期型。

 boolean:布尔型(true或false)。

流程控制语句

条件语句

if <表达式> then
    语句块;
end if;

if <表达式> then
    语句块1;
else
    语句块2;
end if;

if <表达式1> then
       语句块1;
elsif <表达式2> then
       语句块2;
else
       语句块3;
end if;

case语句

case <表达式>
    when <表达式1> then 语句块1;
    when <表达式2> then 语句块2;
      ……
    when <表达式n> then 语句块n;
   [ else 语句块 n+1 ]
end ;

循环语句

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

while 循环条件
loop 
    循环体;
end loop;

for 循环控制变量 in [reverse] 循环下限 .. 循环上限 
loop
  循环体;
end loop

for循环示例

declare 
   sum_num number := 0;
begin
   for i in 1..100 loop
     sum_num := sum_num + i;
   end loop;
   dbms_output.put_line(sum_num);
end;

%TYPE变量

%TYPE用来定义与数据表中指定字段数据类型相同的变量。如果表中字段的数据类型或长度发生变化,%TYPE变量会自动随之变化。这样用户不必查看表中各个字段的数据类型,就可以确保所定义的变量能够正确存储字段数据。

变量名 方案名.表名.字段名%type

var_empno scott.emp.empno%type; --员工编号

自定义记录类型变量

自定义记录数据类型可以表示由多个字段值组成的一行数据。使用时首先要定义记录类型的结构,然后声明该类型的变量存储数据。

type 类型名称 is record (
     变量名称  数据类型 := 默认值 , 
     ……
     变量名称  数据类型 := 默认值 
) ;

示例1:输入职工编号,输出其姓名与工资

declare
    type employee_type is record (  --声明自定义记录类型
         ename  scott.emp.ename%type, 
         sal  scott.emp.sal%type
);
    var_emp employee_type ; --声明自定义记录类型变量
    var_empno scott.emp.empno%type;
begin
    var_empno:=&var_empno;
    select ename, sal into var_emp from scott.emp where  empno=var_empno ; 
     dbms_output.put (var_emp.ename) ;
     dbms_output.put_line(' '||var_emp.sal) ; 
end; 

示例2:允许同一记录类型的两个变量整体赋值。

declare
    type employee_type is record ( 
         ename  scott.emp.ename%type, 
         sal  scott.emp.sal%type
);
    var_emp1 employee_type ;
    var_emp2 employee_type ; 
    var_empno scott.emp.empno%type;
begin
    var_empno:=&var_empno;
    select ename, sal into var_emp1 from scott.emp where empno=var_empno ;
    var_emp2:=var_emp1; 
    dbms_output.put (var_emp2.ename) ;
    dbms_output.put_line(' '||var_emp2.sal) ; 
end; 

%ROWTYPE变量

%ROWTYPE类型可以根据数据表的行结构定义数据类型,用于存储从数据表中检索到的一行完整的数据。如果数据库表的结构发生变化,记录变量中的结构也将随之改变。

变量名 方案名.表名%rowtype

示例:输入职工编号,输出其详细信息

declare
    var_emp  scott.emp%rowtype ; --数据行变量
    var_empno scott.emp.empno%type;
begin
    var_empno:=&var_empno;
    select * into var_emp from scott.emp where  empno=var_empno ;
    dbms_output.put (var_emp.ename) ;
    dbms_output.put_line(' '||var_emp.job) ; 
    dbms_output.put_line(' '||var_emp.sal) ;
end; 

异常处理

PL/SQL程序运行时出现的错误称为异常。可以分为两类:

 预定义异常: 当 PL/SQL 程序违反 Oracle 规则或超越系统限制时隐式引发

 用户自定义异常:用户可以声明自定义异常,自定义的异常通过 raise 语句手动引发

系统预定义异常

begin
     过程及sql语句;
exception
     when 异常名称then
          过程及sql语句;
     when others then
          过程及sql语句;
end;
命名的系统异常 产生原因
ACCESS_INTO_NULL 未定义对象 ,视图给一个没有初始化的对象赋值
CASE_NOT_FOUND CASE 中若未包含相应的 WHEN ,并且没有设置 ELSE 时
COLLECTION_IS_NULL 集合元素未初始化
CURSER_ALREADY_OPEN 游标已经打开
DUP_VAL_ON_INDEX 唯一索引对应的列上有重复的值
INVALID_CURSOR 在不合法的游标上进行操作
INVALID_NUMBER 内嵌的 SQL 语句不能将字符转换为数字 ,即将一个非有效的字符串转换成数字
NO_DATA_FOUND 使用 select into 未返回行,或应用索引表未初始化的元素时 ;查询语句无返回数据,或者引用了一个被删除的元素,或者引用了一个没有被初始化的元素
TOO_MANY_ROWS 执行 select into 时,结果集超过一行
ZERO_DIVIDE 除数为 0
SUBSCRIPT_BEYOND_COUNT 元素下标超过嵌套表或 VARRAY 的最大值
SUBSCRIPT_OUTSIDE_LIMIT 使用嵌套表或 VARRAY 时,将下标指定为负数
VALUE_ERROR 赋值时,变量长度不足以容纳实际数据
LOGIN_DENIED PL/SQL 应用程序连接到 oracle 数据库时,提供了不正确的用户名或密码
NOT_LOGGED_ON PL/SQL 应用程序在没有连接 oralce 数据库的情况下访问数据
PROGRAM_ERROR PL/SQL 内部问题,可能需要重装数据字典& pl./SQL 系统包
ROWTYPE_MISMATCH 宿主游标变量与 PL/SQL 游标变量的返回类型不兼容
SELF_IS_NULL 使用对象类型时,在 null 对象上调用对象方法
STORAGE_ERROR 运行 PL/SQL 时,超出内存空间
SYS_INVALID_ID 无效的 ROWID 字符串
TIMEOUT_ON_RESOURCE Oracle 在等待资源时超时

可以使用others子句捕获各种异常,同时结合两个函数向用户报告信息:

 sqlcode函数:返回出错码

 sqlerrm函数:返回出错信息

begin
  insert into scott.emp(empno , ename , job , sal , deptno)
  values('7999','ATG','CLERK',1500,'ABC');
exception
  when dup_val_on_index then
    dbms_output.put_line('捕获dup_val_on_index异常');
    dbms_output.put_line('该主键值已经存在');
  when others then  -- others子句必须放在各种异常处理的最后
    dbms_output.put_line( '错误号:' || sqlcode );
    dbms_output.put_line( '错误描述:' ||sqlerrm );
end;

用户自定义异常

程序开发人员可以根据具体的业务逻辑规则,自定义特定异常。当用户操作违反了业务逻辑规则后,可以手动引发该异常,从而中断程序的正常执行,转到自定义的异常处理代码。

自定义异常的处理步骤:

 定义异常处理:异常名 exception ;

 触发异常处理:raise 异常名

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容