.Net程序员学用Oracle系列(26):PLSQL 之类型、变量和结构

时间:2023-02-18 23:33:34

1、类型

《.Net程序员学用Oracle系列(5):三大数据类型》一文中详细地讲述了 Oracle 的基本数据类型,文中还提到,除基本数据类型之外,Oracle 还在语法上支持一些非固有数值类型。

事实上,Oracle 在语法上支持的数据类型远不止于此,Oracle 还支持一些复杂而强大的数据类型。如属性类型%TYPE%ROWTYPE,记录类型 RECORD,集合类型 VARRAY 和 TABLE 等。本节将会介绍实用的属性类型和灵活的记录类型。

1.1、属性类型

属性类型是一种可以直接引用数据库中列的数据类型来描述变量类型的类型。Oracle 提供了两种属性类型,分别是%TYPE%ROWTYPE,下文将逐一说明。

%TYPE:该属性允许在声明中引用数据库中的列或先前声明的变量的数据类型,而不是硬编码类型名称。在声明常量、变量和参数时,都可以使用%TYPE属性作为数据类型说明符。如果引用的类型被更改,则声明也将自动更新,这点有利于后期代码维护。

示例:

DECLARE
v_staff_name t_staff.staff_name%TYPE;
BEGIN
SELECT t.staff_name INTO v_staff_name FROM demo.t_staff t WHERE t.staff_id=5;
DBMS_OUTPUT.PUT_LINE(v_staff_name);
END;

%ROWTYPE:该属性可以表示数据库中表或游标的行的记录类型。使用%ROWTYPE声明的变量可以存储从表中选择或从游标或游标变量获取的整行数据,且变量记录中的字段和每行中的相应列具有相同的名称和数据类型。

示例:

DECLARE
v_staff t_staff%ROWTYPE;
BEGIN
SELECT t.* INTO v_staff FROM demo.t_staff t WHERE t.staff_id=5;
DBMS_OUTPUT.PUT_LINE(v_staff.staff_name);
END;

想要更多的了解属性类型可参考:《Database PL/SQL User's Guide and Reference: %TYPE Attribute》《Database PL/SQL User's Guide and Reference: %ROWTYPE Attribute》

1.2、记录类型

记录类型是由单行多列标量构成的复合结构。可以看做是一种用户自定义的数据类型,提供了将一个或多个标量封装成一个对象进行操作的能力。在使用记录数据类型的变量时,需要在声明部分先定义记录的成员变量,然后在执行部分引用该记录变量本身或其中的成员。但不可以对记录做整体性的比较运算,如判断记录类型的变量是否为 NULL。

示例:

DECLARE
TYPE staff_type IS RECORD(
staff_name VARCHAR2(50),
gender VARCHAR2(2)
);
v_staff staff_type;
BEGIN
SELECT t.staff_name,DECODE(t.gender,1,'男',0,'女','两性') INTO v_staff.staff_name,v_staff.gender
FROM demo.t_staff t WHERE t.staff_id=5;
DBMS_OUTPUT.PUT_LINE(v_staff.staff_name||'|'||v_staff.gender);
END;

记录类型和%ROWTYPE属性在用途上比较相似,区别在于前者是自定义结构,而后者为表结构,前者比较灵活,而后者比较方便。

2、变量

一般计算机编程语言中都有变量的概念,PL/SQL 也不例外,变量用于存储计算结果和表示可变状态,本节将着重介绍在 PL/SQL 中变量如何定义及赋值。另外,PL/SQL 中也有常量,只是极少有人使用,有兴趣的读者可以参考《Oracle Database PL/SQL Language Reference: Constant》

2.1、变量类型

在 PL/SQL 中定义变量的可选类型非常多,包括 Oracle 中常见的三大类基本数据类型,以及 Oracle 在语法上支持的诸多非固有数据类型。如整形(INT/INTEGER)、布尔类型(BOOLEAN)等 Oracle 本身并不支持,却在 PL/SQL 中可用的数据类型。

2.2、变量定义

在 PL/SQL 中定义变量与 C# 中定义变量本质上并无区别,不同的是 PL/SQL 中的变量得集中定义,变量定义区域得用DECLARE关键字开头,且每行只能定义一个变量。如果是 SQL*Plus 环境则必须用VAR[IABLE]开头。

语法:

variable_name datatype [[NOT NULL] {:= | DEFAULT} expression];

如果使用了 NOT NULL 则必须给变量赋初始值。另外,在命名变量的时候还需要遵守以下命名规则:

  • 1、变量名必须以字母开头。
  • 2、变量名长度不能超过 30 个字符。
  • 3、变量名中不能含有空格。
  • 4、同一语句块内变量名不能重复。
  • 5、变量名不能与查询中的列名相同。

2.3、变量赋值

给 PL/SQL 变量赋值的写法与给 C# 字段赋值写法基本一样,既可以在定义变量时就赋给它一个初始值,也可以在使用之前给它赋值,如果不赋值也会有默认值。唯一的区别就是,C# 中类型不同默认值也不同,而 PL/SQL 中所有类型的默认值都一样,都是 NULL。

在 PL/SQL 中还有一点比较怪的就是,可能所有编程语言的赋值操作符都是=,而 PL/SQL 中的赋值操作符却偏偏是:=。而且 Oracle 相关的 API 中参数写法也与其它大多数数据库不同。

示例 1(普通 PL/SQL 环境):

DECLARE
v1 NUMBER;
v2 NUMBER(5,2);
v3 NUMBER := 50.20;
v4 NUMBER(4) := 1998;
v5 VARCHAR2(4) DEFAULT 'A';
v6 DATE NOT NULL := fn_now;
BEGIN
v1 := 100;
v2 := 99.99;
v5 := 'A5';
v6 := SYSDATE;
DBMS_OUTPUT.PUT_LINE(v1||'|'||v2||'|'||v3||'|'||v4||'|'||v5||'|'||v6);
END;

示例 2(SQL*Plus 环境):

VARIABLE v1 NUMBER
BEGIN
:v1 := 12;
DBMS_OUTPUT.PUT_LINE(:v1);
END;
/

3、结构

和普通编程语言一样,PL/SQL 中也有常见的三大控制结构以及顺序控制语句——GOTO。本节将重点讲述被广泛接受的三大控制结构,至于不受待见的 GOTO 语句,有兴趣的读者可以参考《Oracle Database PL/SQL User's Guide and Reference: Using the GOTO Statement》

3.1、顺序结构

顺序结构是面向过程编程中最基本、最简单、最常用的程序控制结构。顺序结构用于表示若干个依次执行的处理步骤,表现形式就是线性结构,一个方向走下去、不拐弯。使用时只要按照解决问题的顺序写出相应的语句就行,它的执行顺序是自上而下、依次执行。

3.2、选择结构

PL/SQL 中提供了两种选择结构,分别是IF结构和CASE结构。其中IF结构有三种变体,CASE结构有两种变体。下文将逐一讲述各个选择语句:

IF 结构变体一:

语法:

IF condition THEN
{...statements to execute when condition is TRUE...}
END IF;

示例:

BEGIN
IF 1>0 THEN
DBMS_OUTPUT.PUT_LINE('executed');
END IF;
END;

IF 结构变体二:

语法:

IF condition THEN
{...statements to execute when condition is TRUE...}
ELSE
{...statements to execute when condition is FALSE...}
END IF;

示例:

BEGIN
IF 1>2 THEN
DBMS_OUTPUT.PUT_LINE('The result is true');
ELSE
DBMS_OUTPUT.PUT_LINE('The result is false');
END IF;
END;

IF 结构变体三:

语法:

IF condition1 THEN
{...statements to execute when condition1 is TRUE...}
ELSIF condition2 THEN
{...statements to execute when condition2 is TRUE...}
[ELSE
{...statements to execute when both condition1 and condition2 are FALSE...}]
END IF;

示例:

BEGIN
IF 1>2 THEN
DBMS_OUTPUT.PUT_LINE('1>2 branch');
ELSIF 1<2 THEN
DBMS_OUTPUT.PUT_LINE('1<2 branch');
ELSE
DBMS_OUTPUT.PUT_LINE('1=2 branch');
END IF;
END;

注意:IF 结构变体三中有个巨坑,就是 IF 和 ELSE 之间的分支写法,不是 ELSE IF 也不是 ELSEIF 而是 ELSIF。尽管你写成 ELSE IF 编辑器也有智能提示,但当你执行的时候就会报ORA-06550的错,而且这个错误的描述正常人基本没可能看懂。

CASE 结构变体一

示例:

DECLARE
v_grade VARCHAR2(1);
BEGIN
v_grade:='B';
CASE v_grade
WHEN 'A' THEN DBMS_OUTPUT.PUT_LINE('甲');
WHEN 'B' THEN DBMS_OUTPUT.PUT_LINE('乙');
WHEN 'C' THEN DBMS_OUTPUT.PUT_LINE('丙');
ELSE DBMS_OUTPUT.PUT_LINE('丁');
END CASE;
END;

CASE 结构变体二

示例:

DECLARE
v_score NUMBER(3);
BEGIN
v_score:=78;
CASE
WHEN v_score>=80 THEN DBMS_OUTPUT.PUT_LINE('优');
WHEN v_score>=70 THEN DBMS_OUTPUT.PUT_LINE('良');
WHEN v_score>=60 THEN DBMS_OUTPUT.PUT_LINE('中');
ELSE DBMS_OUTPUT.PUT_LINE('差');
END CASE;
END;

当 CASE 语句用于查询时,如在 SELECT 子句列表中,END 后面就不能带 CASE 了。与多分支的 IF 语句相比,CASE 语句更可读、更高效,所以当程序分支较多时,应尽可能的使用 CASE 而不是 IF。CASE 语句的 ELSE 子句是可选的。但如果省略 ELSE 字句,PL/SQL 将为 CASE 语句添加以下隐式的 ELSE 子句:

ELSE RAISE CASE_NOT_FOUND;

换句话说,如果你省略了 ELSE 子句,且 CASE 语句与 WHEN 子句不匹配,PL/SQL 就会引发预定义的异常CASE_NOT_FOUND

3.3、循环结构

PL/SQL 中提供了三种循环结构,分别是LOOPWHILE LOOPFOR LOOP。另外 PL/SQL 还提供了EXIT语句用于退出当前循环。下文将逐一讲述各个循环语句:

LOOP 循环

语法:

LOOP
{...statements...}
EXIT [ WHEN boolean_condition ];
END LOOP;

示例 1:

DECLARE
v_counter BINARY_INTEGER := 0;
BEGIN
LOOP
v_counter := v_counter + 1;
DBMS_OUTPUT.PUT_LINE(v_counter); -- 输出结果:1、2、3、4、5、6、7、8、9
IF v_counter >= 9 THEN
EXIT;
END IF;
-- 上面的 IF 语句块还可以由“EXIT WHEN v_counter >= 9;”代替
END LOOP;
END;

示例 2(嵌套循环):

DECLARE
i BINARY_INTEGER := 0;
j BINARY_INTEGER := 0;
BEGIN
LOOP
i := i + 1;
j := 0; LOOP
j := j + 1;
DBMS_OUTPUT.PUT_LINE('i*j=('||i||'*'||j||')='||i*j);
EXIT WHEN j >= 3;
END LOOP; EXIT WHEN i >= 4;
END LOOP;
END;

示例 3(标记循环):

DECLARE
i BINARY_INTEGER := 0;
j BINARY_INTEGER := 0;
BEGIN
<<outer_loop>>
LOOP
i := i + 1;
j := 0;
<<inner_loop>>
LOOP
j := j + 1;
DBMS_OUTPUT.PUT_LINE('i*j=('||i||'*'||j||')='||i*j);
EXIT inner_loop WHEN j >= 3;
EXIT outer_loop WHEN i >= 4;
END LOOP inner_loop;
END LOOP outer_loop;
END;

WHILE LOOP 循环

语法:

WHILE condition
LOOP
{...statements...}
END LOOP;

示例:

DECLARE
v_score NUMBER(3) := 0;
BEGIN
WHILE v_score < 60 LOOP
v_score := v_score + 10;
DBMS_OUTPUT.PUT_LINE(v_score); -- 输出结果:10、20、30、40、50、60
END LOOP;
DBMS_OUTPUT.PUT_LINE('over');
END;

FOR LOOP 循环

语法:

FOR loop_counter IN [REVERSE] lowest_number..highest_number
LOOP
{...statements...}
END LOOP;

示例 1(正向循环):

BEGIN
FOR i IN 3..7 LOOP
DBMS_OUTPUT.PUT_LINE(i); -- 输出结果:3、4、5、6、7
END LOOP;
END;

示例 2(反向循环):

BEGIN
FOR i IN REVERSE 3..7 LOOP
DBMS_OUTPUT.PUT_LINE(i); -- 输出结果:7、6、5、4、3
END LOOP;
END;

示例 3(遍历输出所有课程和备注):

BEGIN
FOR course IN(
SELECT t.course_name,t.course_desc FROM demo.t_course t
)
LOOP
DBMS_OUTPUT.PUT_LINE(course.course_name||'('||course.course_desc||')');
END LOOP;
END;

注意:FOR LOOP 循环中的计数器(变量)可以被读取,但不能被修改。另外,在 LOOP 循环的示例中用到的 EXIT 和循环标记,同样可用于 WHILE LOOP 循环和 FOR LOOP 循环中。

4、总结

本文主要讲述了 PL/SQL 中的变量和结构两个基本的编程元素,以及属性类型和记录类型。

本文链接http://www.cnblogs.com/hanzongze/p/oracle-plsql-1.html

版权声明:本文为博客园博主 韩宗泽 原创,作者保留署名权!欢迎通过转载、演绎或其它传播方式来使用本文,但必须在明显位置给出作者署名和本文链接!本人初写博客,水平有限,若有不当之处,敬请批评指正,谢谢!

.Net程序员学用Oracle系列(26):PLSQL 之类型、变量和结构的更多相关文章

  1. &period;Net程序员学用Oracle系列&lpar;1&rpar;:导航目录

    本人从事基于 Oracle 的 .Net 企业级开发近三年,在此之前学习和使用的都是 (MS)SQL Server.未曾系统的了解过 Oracle,所以长时间感到各种不习惯.不方便.怪异和不解,常会遇 ...

  2. &period;Net程序员学用Oracle系列&lpar;2&rpar;:准备测试环境

    <.Net程序员学用Oracle系列:导航目录> 本文大纲 1.创建说明 1.1.为什么要创建的测试环境? 1.2.了解 Oracle 实例的默认用户 2.创建环境 2.1.创建基本环境 ...

  3. &period;Net程序员学用Oracle系列&lpar;6&rpar;:表、字段、注释、约束、索引

    <.Net程序员学用Oracle系列:导航目录> 本文大纲 1.表 1.1.创建表 1.2.修改表 & 删除表 2.字段 2.1.添加字段 2.2.修改字段 & 删除字段 ...

  4. &period;Net程序员学用Oracle系列&lpar;7&rpar;:视图、函数、过程、包

    <.Net程序员学用Oracle系列:导航目录> 本文大纲 1.视图 1.1.创建视图 2.函数 2.1.创建函数 2.2.调用函数 3.过程 3.1.创建过程 3.2.调用过程 4.包 ...

  5. &period;Net程序员学用Oracle系列&lpar;8&rpar;:触发器、任务、序列、连接

    <.Net程序员学用Oracle系列:导航目录> 本文大纲 1.触发器 1.1.创建触发器 1.2.禁用触发器 & 启用触发器 & 删除触发器 2.任务 2.1.DBMS_ ...

  6. &period;Net程序员学用Oracle系列&lpar;9&rpar;:系统函数(上)

    <.Net程序员学用Oracle系列:导航目录> 本文大纲 1.字符函数 1.1.字符函数简介 1.2.语法说明及案例 2.数字函数 2.1.数字函数简介 2.2.语法说明及案例 3.日期 ...

  7. &period;Net程序员学用Oracle系列&lpar;10&rpar;:系统函数(下)

    <.Net程序员学用Oracle系列:导航目录> 本文大纲 1.转换函数 1.1.TO_CHAR 1.2.TO_NUMBER 1.3.TO_DATE 1.4.CAST 2.近似值函数 2. ...

  8. &period;Net程序员学用Oracle系列&lpar;11&rpar;:系统函数(下)

    1.聚合函数 1.1.COUNT 函数 1.2.SUM 函数 1.3.MAX 函数 1.4.MIN 函数 1.5.AVG 函数 2.ROWNUM 函数 2.1.ROWNUM 函数简介 2.2.利用 R ...

  9. &period;Net程序员学用Oracle系列&lpar;15&rpar;:DUAL、ROWID、NULL

    1.DUAL 表 2.ROWID 类型 2.1.利用 ROWID 查询数据 2.2.利用 ROWID 更新数据 3.NULL 值 3.1.NULL 与空字符串 3.2.NULL 与函数 3.3.NUL ...

随机推荐

  1. ie8下背景图片平铺问题

    IE9+及其他浏览器实现背景图片平铺可能需要一个属性就可以background-size:100%/cover; 但是ie8下background-size是不兼容的,因此我们需要用到滤镜,来解决背景 ...

  2. java 平台 权限管理

    最近在做公司内部的物流业务平台的权限管理,感触颇多.记录一下 权限管理分两部分:数据权限和操作权限. 数据权限: 这个是和用户相关的. 因为平台是多机构的,所以再考虑数据权限的时候,是按照机构来管理的 ...

  3. Zabbix日志监视的汇总报警(更新发送邮件脚本)

    Zabbix的用户一定会碰到这种情况: 日志报警一般设置的是multiple模式,有错误大量写入的时候,每写入一行就会触发一次action,导致出现大量的报警邮件. 特别是ora的报警,经常一出就是上 ...

  4. 转:SQL子句的执行顺序

    SQL 不同于与其他编程语言的最明显特征是处理代码的顺序.在大数编程语言中,代码按编码顺序被处理,但是在SQL语言中,第一个被处理的子句是FROM子句,尽管SELECT语句第一个出现,但是几乎总是最后 ...

  5. LintCode Kth Largest Element

    原题链接在这里:http://www.lintcode.com/en/problem/kth-largest-element/# 在LeetCode上也有一道,采用了标准的quickSelect 方法 ...

  6. 学习opencv跟轮廓相关的

    查找轮廓 轮廓到底是什么?一个轮廓一般对应一系列的点,也就是图像中的一条曲线.表示的方法可能根据不同情况而有所不同.有多重方法可以表示曲线.在openCV中一般用序列来存储轮廓信息.序列中的每一个元素 ...

  7. (转)solr排序OOM解决方法

    转自 http://topcat.iteye.com/blog/1293650 问题 lucene使用排序时会将被排序字段全部加入内存再进行排序,当多次使用不同字段进行排序时会造成OOM问题 解决方案 ...

  8. &lbrack;Locked&rsqb; Paint House I &amp&semi; II

    Paint House There are a row of n houses, each house can be painted with one of the three colors: red ...

  9. 对actuator的管理端点进行ip白名单限制(springBoot添加filter)

    在我们的SpringCloud应用中,我们会引入actuator来进行管理和监控我们的应用 常见的有:http://www.cnblogs.com/yangzhilong/p/8378152.html ...

  10. 如何使用Android Studio把自己的Android library分享到jCenter和Maven Central

    参考链接: http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0623/3097.html