Oracle学习10:PLSQL学习

1. PLSQL概述

PLSQL是Oracle内部的一种编程语言
PLSQL是一门语言。叫作过程化SQL语言(Procedural Language SQL)
PLSQL是一种过程化语言,属于第三代语言,它与C、C++、Java等语言同样关注于处理细节,能够用来实现比较复杂的业务逻辑。
PL/SQL是对结构化查询语言(SQL)的过程语言扩展。java

PL/SQL的基本单位叫作一个区段,由三个部分组成:一个声明部分,一个可运行部分,和排除-构建部分。
PL/SQL区段只被编译一次而且以可运行的形式储存,以下降响应时间web

实际工做中,PLSQL多用于写触发器、存储过程、函数等sql

2. PLSQL基本语法

PL/SQL的基本单位叫作一个区段,由三个部分组成:一个声明部分,一个可运行部分,和排除-构建部分。
这里声明只有运行部分是必须的,其他均是可选,以下:数据库

declare --(可选,声明变量用)
begin --(must) null;
exception --(可选)
end;    --(must)

2.1 简单的PSQL语句块

2.1.1 null语句块

以下咱们建立一个最简单的PLSQL语句块编程

begin null;
end;
/

在命令行执行,结果以下:
这里写图片描述数组

须要指出的是,这里的 null 不能够省略,PLSQL语句块中必须包含一条语句bash

2.1.2 Hello World语句

下面咱们写一个最简单而且打印Hello World的PLSQL。编程语言

set serveroutput on;    --用于打开控制台输出服务,默认是off,则不打印
begin dbms_output.put_line('HelloWorld');  --相似于java的System.out.print
end;
/

在命令行执行,结果:
这里写图片描述svg

2.2 含有声明(declare)语句的PLSQL

declare用于声明变量。
变量名称与变量类型不可省略,默认值经过 := 赋值,默认值能够省略。
格式以下:函数

declare
    v_variable1 variable_type [ := default_value];
    v_variable2 variable_type [ := default_value];

2.2.1 PLSQL变量类型

2.2.1.1 变量命名

变量声明
规则:

  • 变量不能使用保留字。如from select;
  • 第一个字符必须是字母,通常以 v_ 开头;
  • 变量名最多包含30个字符;
  • 不要与数据库的表或者列同名;
  • 每一行只能声明一个变量。

2.2.1.2 基本变量类型7个

PLSQL的基本变量类型有7个。以下:

  • binary_integer:整数,主要用来计数而不是用来表示字段类型。
  • number:数字类型
  • char:定长字符串
  • varchar2:变长字符串
  • date:日期
  • long:长字符串,最长2GB
  • boolean:布尔类型,能够取值true、false和null

根据以上类型,咱们写一个含有变量声明的PLSQL。

declare
    v_temp number(1);
    v_count binary_integer := 0;
    v_sal number(7,2) :=4000.00;
    v_date date := sysdate;
    v_pi constant number(3,2) := 3.14;  --相似于Java的final关键字,表示常量
    v_valid boolean := false;
    v_name varchar2(20) not null := 'MyName';
begin
    dbms_output.put_line('v_temp value: '||v_temp);  
    dbms_output.put_line('v_count value: '||v_count);
    dbms_output.put_line('v_sal value: '||v_sal);
    dbms_output.put_line('v_date value: '||v_date);
    dbms_output.put_line('v_pi value: '||v_pi);
    -- dbms_output.put_line('v_valid value: '||v_valid); --不能打印boolean值
    dbms_output.put_line('v_name value: '||v_name);
end;

命令行结果以下:
这里写图片描述

这里须要指出的是,PLSQL的dbms_output.put_line()函数不能直接打印boolean类型

2.2.1.3 关联变量类型(关联别的字段、变量的变量类型)

在工做中,变量的做用更多的是存储某个字段中的值,于是保证变量数据类型与字段数据类型一致是颇有必要的。
PLSQL中,在变量声明时,能够经过%type得到指定字段(变量)的数据类型。这种方式能够确保当源表的字段类型改变时,变量的类型自动改变。
以下:

declare
    v_empno number(4);
    v_empno2 emp.empno%type; --v_empno2的类型是表emp的empno字段的类型
    v_empno3 v_empno2%type; --v_empno3的类型是变量v_empno2的类型

2.2.1.4 复合变量

PLSQL提供了两种复合变量。

  • Table 相似于Java的数组Array
  • record 相似于Java的类Class

以下声明一个Table变量

declare
    type type_table_emp_empno is table of emp.empno%type index by binary_integer;   --声明一个数组类型
        v_empnos type_table_emp_empno;  --利用类型声明变量
begin
    v_empno(0) := 7369;
    v_empno(2) := 7839;
    v_empno(-1) := 9999;    --Table类型的下标能够是负数
    dbms_output.put_line(v_empno(-1));
end;

声明一个Record类型

--RECORD类型
declare 
    type type_record_dept is record
        (
            deptno dept.deptno%type,
            dname dept.dname%type,
            loc dept.loc%type
        );
        v_temp type_record_dept;
begin
    v_temp.deptno := 50;
    v_temp.dname := 'aaa';
    v_temp.loc := 'bj';
    dbms_output.put_line(v_temp.deptno||v_temp.dname||v_temp.loc);
end;

这里若是表的结构发生了变化,咱们如何保证声明的复合变量和表中的变量相同呢,能够经过rowtype实现。

%rowtype:定义一个表示表中一行记录的变量
以下:

declare 
    v_temp dept%rowtype;
begin
    v_temp.deptno := 50;
    v_temp.dname := 'aaa';
    v_temp.loc := 'bj';
    dbms_output.put_line(v_temp.deptno||v_temp.dname||v_temp.loc); end;

这里关于 %type%rowtype 作一个简单的对比说明:
以下:
%TYPE
为了使一个变量的数据类型与另外一个已经定义了的变量(尤为是表的某一列)的数据类型相一致,当被参照的那个变量的数据类型改变了以后,这个新定义的变量的数据类型会自动跟随其改变,无需修改PL/SQL程序。当不能确切地知道被参照的那个变量的数据类型时,就只能采用这种方法定义变量的数据类型。

%ROWTYPE
若是一个表有较多的列,使用%ROWTYPE来定义一个表示表中一行记录的变量,比分别使用%TYPE来定义表示表中各个列的变量要简洁得多,而且不容易遗漏、出错。这样会增长程序的可维护性。当表的某些列的数据类型改变了以后,这个新定义的变量的数据类型会自动跟随其改变,无需修改PL/SQL程序。当不能确切地知道被参照的那个表的结构及其数据类型时,就只能采用这种方法定义变量的数据类型。


2.3 含有异常处理的PLSQL

异常块经过exception关键字声明,以下:

declare
  v_num number := 0;
begin
  v_num := 2/v_num;
  dbms_output.put_line(v_num);
exception
  when others then
    dbms_output.put_line('error');
end;
/

3.PLSQL中的SQL语法

3.1 DML语句(数据操做语言,select,update,insert,delete from)

PLSQL中能够写SQL语句,和平时的SQL语法基本一致,须要注意的是如下几点:

  • select语句必须返回一条记录,而且只能返回一条记录;
  • select必须和into一块用(除非使用游标);
  • 操做的数据能够是变量。
  • insert、update、delete等语句与普通SQL完成一致,只是能够传入参数而已。

这里须要解释的是,select语句不返回值则无心义,返回许多则变量装不了。

下面咱们有一张测试表,而且根据这张表,实验理解PLSQL的SQL语句。
这里写图片描述

set serveroutput on;    --该服务开启一次便可,若是以前开启过无需再次开启。
declare
    v_dep ljb_test.dep%type;    --type
    v_name ljb_test.name%type;  --type
    v_test ljb_test%rowtype;    --rowtype

    --测试insert&update的数据
    v_new_dep ljb_test.dep%type := 10;
    v_new_name ljb_test.name%type := '小明';
    v_new_salary ljb_test.salary%type := 5000;

    v_anoter_name ljb_test.name%type := '大明';
begin --select语句必须返回一条记录,而且只能返回一条记录 select dep, name into v_dep, v_name from ljb_test where salary = 6000;  --限定条件必定要保证返回一条记录
    dbms_output.put_line(v_dep||v_name);

    select * into v_test from ljb_test where salary = 6000; --限定条件必定要保证返回一条记录
    dbms_output.put_line(v_test.dep||v_test.name||v_test.salary);

    --insert,与普通SQL语句一致,只是能够插入变量
    insert into ljb_test values(v_new_dep, v_new_name, v_new_salary);
    commit;     --执行完插入语句记得提交事务

    --update,与普通SQL语句一致,只是能够插入变量
    update ljb_test t set name = v_anoter_name where t.name = v_new_name;

    --delete,与普通SQL语句一致,只是能够插入变量
    delete from ljb_test t where t.dep = '1';  --删除部门1的记录

end;
/

输出结果:
这里写图片描述

同时咱们看下更新后的测试表:
这里写图片描述
能够看到数据已经更新。

3.2 DDL(数据定义语言)

DDL语句不能直接执行,必须使用 execute immediate '' 进行包裹;
以下,咱们经过PLSQL建立一张表:

begin execute immediate 'create table tb(aaa varchar2(255) default ''asd'')'; --两个单引号表明一个单引号
end;
/

须要指出的两点:

  • DDL必须被execute immediate ''包裹,不然报错ORA-06550;
  • 平时的语句中的单引号必须经过两个单引号进行表示,如以上脚本的默认值。
  • delete语句能够不使用execute immediate包裹。

咱们在命令行执行,并查看该表
这里写图片描述
一样的,truncate和drop写法以下:

begin execute immediate 'truncate table tb'; --删除记录,释放表空间
    execute immediate 'drop table tb'; --删除记录及定义,释放表空间 end; /

可是有个例外:就是delete。
delete既可使用execute也能够不使用,以下两种写法均是正确的。

begin execute immediate'delete tb';   --delete删除记录,但不释放表空间
    commit;

    delete tb;
    commit;
end;
/

这里能够这样理解,实际上delete table_name 等价于 delete from table_name,是一种DML语言,于是能够直接执行。


这里针对drop、truncate、delete在复习下三者的区别

  • TRUNCATE TABLE:删除内容、释放空间但不删除定义。
  • DELETE TABLE:删除内容不删除定义,不释放空间。
  • DROP TABLE:删除内容和定义,释放空间。

4.PLSQL的判断循环语句

判断循环语句是PLSQL的重要语句。

4.1 判断(分支)语句

判断语句也叫作分支语句,经过if实现。以下经过一个简单实例进行理解:
对于以下表:
这里写图片描述
咱们写一个薪水等级判断语句:
<3000 low
3000~5000 middle
其他 high

--if语句
declare 
    v_sal ljb_test.salary%type;
begin
    select salary into v_sal from ljb_test where name = 'ri';
    if(v_sal < 3000) then 
        dbms_output.put_line('low');
    elsif(v_sal < 5000) then                --elsif
        dbms_output.put_line('middle');
    else                                    --else后面没有then
        dbms_output.put_line('high');
    end if;
end;
/
set serveroutput on;    --也能够放在下面,可是须要在以前增长一个 /
/

和Java等语句的if语句极其类似,不一样的是具体的语法,须要注意其特色。
输出结果以下:
这里写图片描述

4.2 循环语句

与Java中的循环语句相似,PLSQL也要拥有相似的三种循环语句。为了方便理解,我使用Java循环的区分方式来区分PLSQL的三种循环。
即:
do while循环
while循环
for循环
对于三种循环,均具备如下特色:
PLSQL的循环必定是以loop开头,以end loop结束

4.2.1 do while循环

先打印,而后在判断条件,以下:

--相似于do while 循环
set serveroutput on;
declare
    i binary_integer := 1;  --声明一个计数变量
begin
    loop                    --循环老是loop开头
        dbms_output.put_line(i);
        i := i + 1;
        exit when(i>=11);   --循环退出语句
    end loop;               --循环老是end loop结束
end;
/

命令行打印结果以下:
这里写图片描述

4.2.2 while循环

先判断,在循环打印

--while循环
declare
    j binary_integer := 1;  --声明一个计数变量
begin
    while j<11 loop         --循环退出语句,循环老是loop开头
        dbms_output.put_line(j);
        j := j+1;
    end loop;               --循环老是end loop结束
end;
/

结果以下:
这里写图片描述

4.2.3 for循环

for循环无需declare块声明变量。
1..10表示1-10,注意是两个点。

--for循环
begin                       --for循环无需declare声明变量
    for k in 1..10 loop     --1..10表示1-10,注意是两个点
        dbms_output.put_line(k);
    end loop;

    for k in reverse 1..10 loop --reverse表示逆序
        dbms_output.put_line(k);
    end loop;
end;
/

结果以下:
这里写图片描述

5. PLSQL异常处理

前面说过,PLSQL是一门过程语言,因此也支持相似于Java的异常处理机制。
须要指出的是,PLSQL的异常处理实际工做中并不经常使用,缘由很简单,为了提升程序的可移植性(用于多个数据库),咱们通常把异常处理放在Java等语言中处理
异常处理的核心应用通常是日志表
因此,下文咱们仅做简单的介绍。

5.1异常处理的是运行时异常,而非编译时异常

首先,明确一点,异常处理只能捕获运行时的异常,编译时的异常没法捕获,于是若是存在编译异常,则程序没法运行,更没法调用异常处理机制。

begin insert into dual values('',''); --插入字段数明显不符合
    exception
        when others then
        dbms_output.put_line('ERROR!');
end;
/

这时,程序没法编译经过,会直接报错:
这里写图片描述

5.2 常见的异常类型及处理

下面,咱们就以前PLSQL中select语句返回一条记录的要求,来写几个简单的异常捕获程序:

5.2.1 too_many_rows

set serveroutput on;
declare
    v_name ljb_test.name%type;
begin
    select name into v_name from ljb_test;

    exception
        when too_many_rows then     --too_many_rows,返回值超过一条
            dbms_output.put_line('返回值太多');
        when others then
            dbms_output.put_line('error');
end;
/

运行程序,结果以下:
这里写图片描述

5.2.2 no_data_found

declare
    v_name ljb_test.name%type;
begin
    select name into v_name from ljb_test where 1 = 0;

    exception
        when no_data_found then     --no_data_found,无数据异常
            dbms_output.put_line('无数据');
        when others then
            dbms_output.put_line('error');
end;
/

结果以下:
这里写图片描述

5.3 日志表建立

异常处理最核心的应用应该就是日志表,而日志表多用于存储过程等。

  • 首先建立一个日志表
create table err_log( err_id number primary key, err_code number, err_msg varchar2(1024), err_date date );
  • 建立sequence
create sequence seq_err_log start with 1 increment by 1;
  • 添加异常块
declare
    v_errcode number;
    v_errmsg varchar2(1024);

    v_name ljb_test.name%type;
begin --insert into dual values('0','2'); --编译时错误是没法经过异常捕获处理的
    select name into v_name from ljb_test;
    --commit;
exception
    when others then
        rollback;   --回滚,取消错误操做的影响
            v_errcode := SQLCODE;   --SQLCODE,关键字,表明出错代码,Oracle的错误代码所有是负数
            v_errmsg := SQLERRM;    --SQLERRM,关键字,表明出错信息
        insert into err_log values(seq_err_log.nextval,v_errcode,v_errmsg,sysdate);
    commit;         --不要忘记commit
end;
/

咱们查询下对应的日志表内容:
这里写图片描述


若是是存储过程当中的日志表,能够添加一个pro_name字段。
插入的时候,直接插入该存储过程的名字便可。


6.写在最后

至此,PLSQL的基本语法已经学习完成,有关存储过程、游标等的介绍,将会另起一文,本文仅对基本语法等作简单学习总结。