ORACLE定义PL/SQL变量

前言:
使用变量可以保存计算机需要处理的数据,为了给该变量分配适当的内存空间,还需要指定数据类型,有的数据类型还需要指定长度,如字符串。
有些类型可以用于建表(如char),有些则不能(如boolean,rowtype)。
同样是字符串,建表时的限制为4000,在脚本中则为3万多。

简单语法:变量名数据类型;
完整语法:变量名 [constant] 变量类型 [not null] [default 值 | :=值]

其中“[ ]”表示可以不写,“|”表示任选其一。

下面给出变量定义及解释,数据类型先用建表时所用的数据类型:
v1 char:
说明:没有给出长度,所以v1只能保存一个字符。超过了则会出错:数字或值错误 : 字符串缓冲区太小。

v2 varchar2(10);
说明:v2最多只能保存10个字符。如果不写长度,会出错:字符串长度限制在范围(1...32767)

v3 number;
说明:v3保存的数字范围非常大,几乎可以认为是没有限制的。

v4 number(5);
说明:v4最多能够保存5位整数。如果有小数,Oracle会自动四舍五入。如果整数部分超过5位,则会报错:数字或值错误 : 数值精度太高。

v5 number(5,2);
说明:v5最多能够保存3位整数,2位小数。如果小数位不止2位,则Oracle会自动四舍五入。整数位超过3位会报错,同上。

v6 date;
说明:可以直接保存sysdate的值;如果是指定日期,则要用to_date来转化。否则报错:文字与格式字符串不匹配。

定义了变量,变量的默认值为空,此时进行计算,结果一定为NULL。所以变量必须初始化。
初始化有三种方式:

v7 constant number := 100;
说明:定义v7为常量,定义时就必须给定值。然后在程序中就不能再对v7进行赋值了,否则会报错:表达式 'V7' 不能用作赋值目标。

v8 number default 10;
说明:定义v8时就给定默认值10。注意,number类型变量的默认值不是0,而是NULL。

v9 number not null := 1000.50;
说明:定义v9变量不能为空,此时必须给出一个不为NULL的值。在运行时发现v9为NULL,则报错:说明为 NOT NULL 的变量必须有初始化赋值。

预测各打印结果,如果代码有错误请先改正:
declare
v1 char;
v2 varchar2(10);
v3 number;
v4 number(5);
v5 number(5,2);
v6 date;
v7 number default 10;
begin
v1:='ab';
v2:='abcd';
v4:=9998.99;
v5:=1000.5555555;
v6:=to_date('2002-10-2','yyyy-mm-dd');
dbms_output.put_line(v1);
dbms_output.put_line(v2);
dbms_output.put_line(v3+100);
dbms_output.put_line(v4);
dbms_output.put_line(v5);
dbms_output.put_line(v6);
dbms_output.put_line(v7+100);
end;

只能在脚本中使用的变量类型:
上面的数据类型,同时在建表时也能使用。而下面的数据类型只能在PL/SQL脚本中使用。
即:boolean,type,rowtype,record,替代变量、table类型。
其中:
·一个变量只能保存一个值,叫做“标量变量”。如:char、type。
·一个变量只能保存多个值,叫做“复合变量”。如:rowtype、record,table。

1、布尔类型 boolean
boolean类型主要表达真或假。可以为boolean类型变量赋值true或false。
主要用于PL/SQL脚本的流程控制。

示例:
declare
v1 boolean;
begin
v1:=1>2;
dbms_output.put_line(v1);
end;

问题:打印的结果是什么,是true、false、1、0?
回答:
都不对。运行时得到错误信息:调用 'PUT_LINE' 时参数个数或类型错误。
这是因为在脚本中不能直接打印boolean类型的值,所以只能在脚本中使用if语句来判断。

修改示例:
declare
v1 boolean;
begin
v1:=1>2;
if(v1)then
dbms_output.put_line('真');
else
dbms_output.put_line('假');
end if;
end;
打印:

2、type类型。
语法:表.字段%TYPE
返回:某个表的字段的实际类型。
作用:
PL/SQL脚本中,有时定义的变量就是存取表中数据的。此时数据类型及长度就需要与字段的定义一致。可以去查看该字段的具体类型,但这样做很麻烦;并且当字段定义改变时,脚本也需要修改。
所以使用type类型来引用该字段的类型,更方便,维护性更好。


示例:根据工号查询员工姓名。
分析:要从emp表中获取姓名,必须要用查询语句。而要从查询语句中获取返回的值,要加上“into 变量”。

declare
v_empno emp.empno%type;
v_ename emp.ename%type;
begin
v_empno:=7900;
select ename into v_ename from emp where empno=7900;
dbms_output.put_line(v_ename);
end;


3、rowtype类型
语法: 表%rowtype
作用: 保存了某一行记录的所有字段的值。


示例:根据工号为7900的员工的以下信息:ename,job,sal,comm,hiredate。
第一种:如果用type类型,定义5个变量保存查询结果。代码略。
第二种:如果用rowtype类型,只需要定义一个rowtype类型的变量。

代码:
declare
v_empno emp.empno%type;
v_row emp%rowtype;
begin
v_empno:=7900;
select * into v_row from emp where empno=7900;
dbms_output.put_line('ename='||v_row.ename);
dbms_output.put_line('job='||v_row.job);
dbms_output.put_line('sal='||v_row.sal);
dbms_output.put_line('comm='||v_row.comm);
dbms_output.put_line('hiredate='||v_row.hiredate);
end;
打印:
ename=JAMES
job=CLERK
sal=950
comm=
hiredate=03-12月-81

说明
·必须select *,或者根据表结构的顺序写出所有字段名。
·使用rowtype类型最主要的限制在于表中不能有大对象字段如:CLOB、BLOB。

4、record类型
record的意思是“记录”。
有时,用type类型会定义太多的变量,用rowtype又会取到自己不关心的数据。
如何只取自己关心的字段的值呢?此时就用record类型来自定义有多少个列。
语法:
type 类型名 is record(
属性名1 属性类型,
属性名2 属性类型
);
注意
1、属性之间定义用的是逗号(,),不是分号(;)。
2、最后一个属性名不需要再用“,”。

使用步骤有两个:
1、先用type自定义一个新的类型,这个类型的变量能够的变量与定义有关。
2、再用这个新的类型来定义一个变量,这个变量才能保存实际的数据。

示例:需求同上。
declare
--自定义一个类型
type myType is record(
xm emp.ename%type, --姓名
gzuo emp.job%type, --工作
gzi emp.sal%type --工资
);
--使用自定义类型来定义变量
myValue myType;
begin
select ename,job,sal into myValue from emp where empno=7844;
dbms_output.put_line('ename='||myValue.xm);
dbms_output.put_line('job='||myValue.gzuo);
dbms_output.put_line('sal='||myValue.gzi);
end;

说明:
· 在查询时必须与自定义类型的属性顺序一一对应。
·myValue中的属性名也是自定义的,不是字段名。

问题:上面的代码比直接用type类型还多些,看不出优势在什么地方。为什么还要用record?
回答:
在项目中,自定义类型通常会放到在包(package,后面会专门学习)中,这样就可以在其它地方直接引用了。

示例:
先在包中自定义类型:
create or replace package my_bao
as
type myType is record(
xm emp.ename%type, --姓名
gzuo emp.job%type, --工作
gzi emp.sal%type --工资
);
end;
然后就可以在块中直接使用该类型,只不过在类型前要加上包名:“my_bao”。
declare
--使用包中自定义类型来定义变量
myValue my_bao.myType;
begin
select ename,job,sal into myValue from emp where empno=7844;
dbms_output.put_line('ename='||myValue.xm);
dbms_output.put_line('job='||myValue.gzuo);
dbms_output.put_line('sal='||myValue.gzi);
end;

5、替代变量
语法: &替换内容
作用:提供用户输入的界面,在用户输入数据以后,先替换PL/SQL脚本,再编译执行。

案例一:输入年龄,然后打印出来
SQL> declare
2 v_age number(3);
3 begin
4 v_age:=&年龄;
5 dbms_output.put_line('您的年龄是 = '||v_age);
6 end;
7 /
输入 年龄 的值: 20
原值 4: v_age:=&年龄;
新值 4: v_age:=20;

说明:在用户输入“20”以后,将“&年龄”替换为了“20”。于是脚本的源代码发生了改变:
declare
v_age number(3);
begin
v_age:=20;
dbms_output.put_line('您的年龄是 = '||v_age);
end;

所以“&”变量并不是一个真正的变量,它不能保存任何内容,只是提供一个字符串替换的功能。


案例二:用户输入姓名并打印。
declare
v_age varchar2(3);
begin
v_age:=&姓名;
dbms_output.put_line('您的姓名是 = '||v_age);
end;
问题:如果用户输入“a”,程序是否能够运行,如果能,打印的结果是什么?
分析:当用户输入“a”,上面代码就是:
declare
v_age varchar2(3);
begin
v_age:=a;
dbms_output.put_line('您的姓名是 = '||v_age);
end;
由于“a”前后没有单引号,所以会被当作“标识符”即当作变量名。
但是a这个变量又没有定义。所以一定会提示如下的错误:
必须说明标识符 'A'

解决的方法就是:在“&”前后加上单引号,即:
declare
v_age varchar2(3);
begin
v_age:='&姓名';
dbms_output.put_line('您的姓名是 = '||v_age);
end;
/

替代变量小结:
·替代变量的作用主要是为匿名块提供一个数据录入的界面。
·替代变量只是完成字符串替换的功能,不能保存值。
·如果替代变量是字符串,则必须加上单引号。

说明:如果是命名块,则通过名称和参数进行调用,此时不会再用替代变量。

6、table类型
table类型叫做“表类型”,又叫“索引表类型”。
注意:都是指PL/SQL脚本中的table类型,而不是数据库的对象。

在学习table类型之前,先看一个任务。

需求:用户输入10,打印男;输入20,打印女。
方法一:用分支语句(if……等)判断。
方法二:请思考,能不能不用判断语句。这个任务同样适用于java学习者。

方法一:
declare
v_sex_code number(2);
v_sex_name char(2);
begin
v_sex_code:=&性别编号;
if(v_sex_code=10)then
v_sex_name:='男';
else
v_sex_name:='女';
end if;
dbms_output.put_line('您的性别是 = '||v_sex_name);
end;

用户输入:10
打印: 男


方法二:使用table类型。
语法:type 类型名 is table of 值类型 index by 索引类型。

说明:
·作用:定义了一个表格结构,这个表格只有2个列用于保存“索引(键)值对”,这样就可以方便地通过索引找到值。
如:10=男、20=女,其中10,20就叫做索引,通过10,20就能够找到性别名称:男和女
·值类型:指实际保存的数据的类型。如男和女。
·索引类型:通常指定为下面两种整数类型之一:binary_integer 或者 pls_integer。区别是:
·Binary_Integer类型变量值计算是由Oracle来执行,不会出现溢出,但是执行速度较慢,因为它是由Oracle模拟执行。
·Pls_Integer的执行是由硬件即直接由CPU来运算,因而会出现溢出,但其执行速度较前者快许多。Oracle旧版本不支持。


需求:用户输入10,打印男;输入20,打印女。
分析:
·根据上面的需求,值是“男”或者“女”,所以只需要char(2)就可以了。
·在数据量不是太大时,可以选择索引的类型为:pls_integer。

代码:
declare
--1.定义表结构
type myTabType is table of char(2) index by pls_integer;
--2.创建表类型变量
sexTab myTabType;

v_sex_code number(2);
v_sex_name char(2);
begin
--3.插入数据:
sexTab (10):='男';
sexTab (20):='女';
--用户输入
v_sex_code:=&性别编号;

--4. 查询数据(如:sexTab (10) 表示“查询索引为10的值,得到 男”)
v_sex_name:=sexTab (v_sex_code);

dbms_output.put_line('您的性别是 = '||v_sex_name);
end;

说明:sexTab在插入数据以后,在内存结构如下:
index 值
10 男
20 女

使用table类型也可以查询多行多列的数据。

需求:查询所有部门

declare
--定义表结构(省略了index by,见说明)

type myType is table of dept%rowtype;
--创建表类型变量
myTab myType;
begin

select * bulk collect into myTab from dept;
for x in 1..myTab.count
loop
dbms_output.put_line(myTab(x).deptno);
dbms_output.put_line(myTab(x).dname);
end loop;

end;

说明:

·本列中myTab的索引是由程序为索引赋值,所以index by语句可以省略。

· 将查询的所有部门放到myTab,索引自动设置为1,2,3…

打印:

10
ACCOUNTING
20
RESEARCH
30
SALES
40
OPERATIONS

探讨:有关PL/SQL表类型中的理解。
·很容易认为PL/SQL表类型象c或Java中的数组类型,因为其中有个关键字叫做“index”(索引),而数组的下标也叫做“index”。
·但是这样无法解释PL/SQL表类型的索引可以不连续,甚至可以为负数的问题。
·我认为,PL/SQL表类型更象是java中的map对象,索引就对应map中的键,多个值之间的键可以不连续,可以为负数,甚至可以是字符串。两者有异曲同工之妙。

示例:用字符串做索引。
declare
--定义表结构
type myTabType is table of char(2) index by varchar2(5);
sexTab myTabType;

v_sex_code varchar2(5);
v_sex_name char(2);
begin
--往表中添加内容
sexTab('nan'):='男';
sexTab('nv'):='女';
--用户输入
v_sex_code:='&性别编号';
v_sex_name:=sexTab(v_sex_code);

dbms_output.put_line('您的性别是 = '||v_sex_name);
end;

输入:nan
输出: 男

问题:用table类型能够保存多个列吗?
回答:
可以。只要将“值类型”换为复合类型就可以了。如下面的table定义就能够保存emp表的所有记录:
type empTabType is table of emp%rowtype index by pls_integer。
注意:empTabType的索引可以是empno,也可以不是。

联想一下下
·在PL/SQL表中,利用PL/SQL表类型,在某些场合也能够避免减少表连接,从而大大提高连接的效率。
·在java中map对象用途非常广泛,可以缓存很多对象(如配置文件对象,常见的查询结果对象等)。两者的用法真的是异曲同工之妙。

7、其它类型
·嵌套表、数组:用得少,不学了。
·游标类型,引用游标类型:在后面学习游标以后再学习。

主要内容小结:
·块结构:变量区、代码区、异常区
·块分类:匿名块、命名块(具体的在后面学习)
·标量类型:char,varchar2,number,date,boolean,type
·复合类型:rowtype,record,table

个人小结:
·保存一个值的变量,可以用具体的数据或者type类型

·保存多个值的变量,可以用rowtype,record。

·保存多行2列的变量,可以用table类型。

·保存多行多列的变量,仍然用table类型,只是数据类型选用rowtype即可。

你可能感兴趣的:(ORACLE定义PL/SQL变量)