前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PL/SQL 编程(一)基础,变量,分支,循环,异常

PL/SQL 编程(一)基础,变量,分支,循环,异常

作者头像
二十三年蝉
发布2018-02-28 11:04:59
1.6K0
发布2018-02-28 11:04:59
举报
文章被收录于专栏:闻道于事闻道于事

SQL和PL/SQL:

SQL 结构化查询语言(Structural Query Language),是用来访问和操作关系型数据库的一种标准通用语言,属于第四代语言(4GL)。可以方便的调用相应语句来去的结果,特点是非过程化,使用的时候不用指明执行的具体方法,不用关注实现的细节,但是某些情况下满足不了复杂业务流程的需求。

PL/SQL是 Procedure Language & Structured Query Language 的缩写。属于第三代语言(3GL),是一种过程化语言。PL/SQL是对SQL语言存储过程语言的扩展,是一种高级数据库程序设计语言,该语言专门用于在各种环境下对Oracle数据库进行访问。除此之外,可以在Oracle数据库的某些客户端工具中,使用PL/SQL语言也是该语言的一个特点。PL/SQL可以向Java一样实现逻辑判断。条件循环和异常处理等。

同传统的SQL相比PL/SQL有以下优点:

1.可以提高程序的运行性能。

2.可以使程序模块化。

3.可以采用逻辑控制语句来控制程序结构。

4.利用处理运行时的错误信息。

5.良好的可移植性。

PL/SQL块

pl/sql的基本单位是块。分为三部分,声明部分,执行部分,异常处理部分。其中执行部分时必须存在的,声明和异常处理可以没有。

代码语言:javascript
复制
--PL/SQL块的结构如下:
 DECLARE  
    --声明部分: 在此声明PL/SQL用到的变量,类型及游标,以及局部的存储过程和函数
  BEGIN
    -- 执行部分:  过程及SQL 语句  , 即程序的主要部分
  EXCEPTION
    -- 执行异常部分: 错误处理
  END;

变量 常量

变量表示的值是可以变化的,常量初始化后,其值不可改变。

需要注意:pl/sql是一种强类型语言。

如果表示常量,必须用CONSTANT关键字。

标量类型变量:

最简单类型的变量,它本身是单一的值,不包含任何的类型组合,标量类型主要包含数值类型,字符类型,布尔类型,日期类型。还有一种特殊的声明变量类型的方式: %type 

引用型变量:

使用%TYPE,利用已存在的数据类型定义新变量的数据类型。最常见的就是把表中字段类型作为变量或常量的数据类型。

使用%TYPE特性的优点在于:

l         所引用的数据库列的数据类型可以不必知道;

l         所引用的数据库列的数据类型可以实时改变,容易保持一致,也不用修改PL/SQL程序。 

代码语言:javascript
复制
DECLARE
   -- 用%TYPE 类型定义与表相配的字段
   TYPE T_Record IS RECORD(
        T_no emp.empno%TYPE,
        T_name emp.ename%TYPE,
        T_sal emp.sal%TYPE );
   -- 声明接收数据的变量
   v_emp T_Record;
BEGIN
   SELECT empno, ename, sal INTO v_emp FROM emp WHERE empno=7788;
   DBMS_OUTPUT.PUT_LINE
    (TO_CHAR(v_emp.t_no)||' '||v_emp.t_name||'  ' || TO_CHAR(v_emp.t_sal));
END;
代码语言:javascript
复制
DECLARE
   v_empno emp.empno%TYPE :=&no;
   Type t_record is record (
        v_name   emp.ename%TYPE,
        v_sal    emp.sal%TYPE,
        v_date   emp.hiredate%TYPE);
   Rec t_record;
BEGIN
   SELECT ename, sal, hiredate INTO Rec FROM emp WHERE empno=v_empno;
   DBMS_OUTPUT.PUT_LINE(Rec.v_name||'---'||Rec.v_sal||'--'||Rec.v_date);
END;

记录型变量:

它把逻辑相关的、分离的、基本数据类型的变量组成一个整体存储起来,它必须包括至少一个标量型或RECORD 数据类型的成员,称作PL/SQL RECORD 的域(FIELD),其作用是存放互不相同但逻辑相关的信息。在使用记录数据类型变量时,需要先在声明部分先定义记录的组成、记录的变量,然后在执行部分引用该记录变量本身或其中的成员。 

该类型可以包含一个或多个成员,每个成员类型可以不同。成员可以是标量类型,也可以是引用类型。记录类型适合处理查询语句中有多个列的情况,比如调用某个表的一行记录时用记录类型变量存储这行记录。

代码语言:javascript
复制
--可以用 SELECT语句对记录变量进行赋值,只要保证记录字段与查询结果列表中的字段相配即可。
 DECLARE 
   TYPE test_rec IS RECORD(
         Name VARCHAR2(30) NOT NULL := '胡勇',
         Info VARCHAR2(100));
   rec_book test_rec;
BEGIN
   rec_book.Name :='胡勇';
   rec_book.Info :='谈PL/SQL编程;';
   DBMS_OUTPUT.PUT_LINE(rec_book.Name||'  ' ||rec_book.Info);
END; 
代码语言:javascript
复制
--一个记录类型的变量只能保存从数据库中查询出的一行记录,若查询出了多行记录,就会出现错误。 
DECLARE
--定义与hr.employees表中的这几个列相同的记录数据类型
   TYPE RECORD_TYPE_EMPLOYEES IS RECORD(
        f_name   hr.employees.first_name%TYPE,
        h_date   hr.employees.hire_date%TYPE,
        j_id     hr.employees.job_id%TYPE);
--声明一个该记录数据类型的记录变量
   v_emp_record RECORD_TYPE_EMPLOYEES;

BEGIN
   SELECT first_name, hire_date, job_id INTO v_emp_record
   FROM employees
   WHERE employee_id = &emp_id;

   DBMS_OUTPUT.PUT_LINE('雇员名称:'||v_emp_record.f_name
             ||'  雇佣日期:'||v_emp_record.h_date
             ||'  岗位:'||v_emp_record.j_id);
END;
使用%ROWTYPE声明记录类型数据

这种声明方式可以直接引用表中的行作为变量类型,同 %type 相似。

使用%ROWTYPE特性的优点在于:

l         所引用的数据库中列的个数和数据类型可以不必知道;

l         所引用的数据库中列的个数和数据类型可以实时改变,容易保持一致,也不用修改PL/SQL程序。 

代码语言:javascript
复制
DECLARE
    v_empno emp.empno%TYPE :=&no;
    rec emp%ROWTYPE;
BEGIN
    SELECT * INTO rec FROM emp WHERE empno=v_empno;
    DBMS_OUTPUT.PUT_LINE('姓名:'||rec.ename||'工资:'||rec.sal||'工作时间:'||rec.hiredate); 
END;

数组类型

是具有相同数据类型的一组成员的集合。每个成员都有一个唯一的下标,它取决于成员在数组中的位置。在PL/SQL中,数组数据类型是VARRAY。 

代码语言:javascript
复制
DECLARE
--定义一个最多保存5个VARCHAR(25)数据类型成员的VARRAY数据类型
   TYPE reg_varray_type IS VARRAY(5) OF VARCHAR(25);
--声明一个该VARRAY数据类型的变量
   v_reg_varray REG_VARRAY_TYPE;

BEGIN
--用构造函数语法赋予初值
   v_reg_varray := reg_varray_type
         ('中国', '美国', '英国', '日本', '法国');

   DBMS_OUTPUT.PUT_LINE('地区名称:'||v_reg_varray(1)||'、'
                                    ||v_reg_varray(2)||'、'
                                    ||v_reg_varray(3)||'、'
                                    ||v_reg_varray(4));
   DBMS_OUTPUT.PUT_LINE('赋予初值NULL的第5个成员的值:'||v_reg_varray(5));
--用构造函数语法赋予初值后就可以这样对成员赋值
   v_reg_varray(5) := '法国';
   DBMS_OUTPUT.PUT_LINE('第5个成员的值:'||v_reg_varray(5));
END;

赋值:赋值要用  := 。

结构控制:

if条件控制:

和Java基本相同。

代码语言:javascript
复制
declare
  a varchar(10);
  b number(10);
  c number(10);
begin
  a := '明';
  dbms_output.put_line(a);

  b := 2;
  c := 3;
  --分支
  if b > c then
    dbms_output.put_line('b大于c');
  elsif b < c then
    dbms_output.put_line('b小于c');
  else
    dbms_output.put_line('b等于c');
  end if;

end;
代码语言:javascript
复制
DECLARE
    v_empno  employees.employee_id%TYPE :=&empno;
    V_salary employees.salary%TYPE;
    V_comment VARCHAR2(35);
BEGIN
   SELECT salary INTO v_salary FROM employees 
   WHERE employee_id = v_empno;
   IF v_salary < 1500 THEN
       V_comment:= '太少了,加点吧~!';
   ELSIF v_salary <3000 THEN
      V_comment:= '多了点,少点吧~!';
   ELSE
      V_comment:= '没有薪水~!';
   END IF;
   DBMS_OUTPUT.PUT_LINE(V_comment);
   exception
     when no_data_found then
        DBMS_OUTPUT.PUT_LINE('没有数据~!');
     when others then
        DBMS_OUTPUT.PUT_LINE(sqlcode || '---' || sqlerrm);        
END;
代码语言:javascript
复制
DECLARE
   v_first_name  VARCHAR2(20);
   v_salary NUMBER(7,2);
BEGIN
   SELECT first_name, salary INTO v_first_name, v_salary FROM employees
   WHERE employee_id = &emp_id;
   DBMS_OUTPUT.PUT_LINE(v_first_name||'雇员的工资是'||v_salary);
   IF v_salary < 10000 THEN
      DBMS_OUTPUT.PUT_LINE('工资低于10000');
   ELSE
      IF 10000 <= v_salary AND v_salary < 20000 THEN
         DBMS_OUTPUT.PUT_LINE('工资在10000到20000之间');
      ELSE
         DBMS_OUTPUT.PUT_LINE('工资高于20000');
      END IF;
   END IF;
END;
代码语言:javascript
复制
DECLARE
   v_first_name  VARCHAR2(20);
   v_hire_date DATE;
   v_bonus NUMBER(6,2);
BEGIN
   SELECT first_name, hire_date INTO v_first_name, v_hire_date FROM employees
   WHERE employee_id = &emp_id;
   IF v_hire_date > TO_DATE('01-1月-90') THEN
      v_bonus := 800;
   ELSIF v_hire_date > TO_DATE('01-1月-88') THEN
      v_bonus := 1600;
   ELSE
      v_bonus := 2400;
   END IF;
   DBMS_OUTPUT.PUT_LINE(v_first_name||'雇员的雇佣日期是'||v_hire_date
                                    ||'、奖金是'||v_bonus);
END;

case语句

代码语言:javascript
复制
DECLARE
  V_grade char(1) := UPPER('&p_grade');
  V_appraisal VARCHAR2(20);
BEGIN
  V_appraisal :=
  CASE v_grade
    WHEN 'A' THEN 'Excellent'
    WHEN 'B' THEN 'Very Good'
    WHEN 'C' THEN 'Good'
    ELSE 'No such grade'
  END;
  DBMS_OUTPUT.PUT_LINE('Grade:'||v_grade||'  Appraisal: '|| v_appraisal);
END;
代码语言:javascript
复制
DECLARE
   v_first_name employees.first_name%TYPE;
   v_job_id employees.job_id%TYPE;
   v_salary employees.salary%TYPE;
   v_sal_raise NUMBER(3,2);
BEGIN
   SELECT first_name,   job_id,   salary INTO
          v_first_name, v_job_id, v_salary
   FROM employees WHERE employee_id = &emp_id;
   CASE
      WHEN v_job_id = 'PU_CLERK' THEN
         IF v_salary < 3000 THEN v_sal_raise := .08;
         ELSE v_sal_raise := .07;
         END IF;
      WHEN v_job_id = 'SH_CLERK' THEN
         IF v_salary < 4000 THEN v_sal_raise := .06;
         ELSE v_sal_raise := .05;
         END IF;
      WHEN v_job_id = 'ST_CLERK' THEN
         IF v_salary < 3500 THEN v_sal_raise := .04;
         ELSE v_sal_raise := .03;
         END IF;
      ELSE
         DBMS_OUTPUT.PUT_LINE('该岗位不涨工资: '||v_job_id);
   END CASE;
   DBMS_OUTPUT.PUT_LINE(v_first_name||'的岗位是'||v_job_id
                                    ||'、的工资是'||v_salary
                                    ||'、工资涨幅是'||v_sal_raise);
END;

循环:

代码语言:javascript
复制
declare
  a varchar(10);
  b number(10);
  c number(10);
  m number(10);
  sname varchar2(10);
begin
  a := '明';
  dbms_output.put_line(a);

  b := 2;
  c := 3;
  --分支
  if b > c then
    dbms_output.put_line('b大于c');
  elsif b < c then
    dbms_output.put_line('b小于c');
  else
    dbms_output.put_line('b等于c');
  end if;
  --循环 1
  loop
    exit when c < 0;
    dbms_output.put_line('loop:' || c);
    c := c - 1;
  end loop;
  --循环 2

  while b > 0 loop
    dbms_output.put_line('while:' || b);
    b := b - 1;
  end loop;
  --循环 3 
  for n in 1 .. 3 loop
    dbms_output.put_line('for:' || n);
  end loop;

end;

异常

编译时的错误不能称为异常。

有三种类型的异常错误:

    1. 预定义 ( Predefined )错误

  ORACLE预定义的异常情况大约有24个。对这种异常情况的处理,无需在程序中定义,由ORACLE自动将其引发。

    2. 非预定义 ( Predefined )错误

   即其他标准的ORACLE错误。对这种异常情况的处理,需要用户在程序中定义,然后由ORACLE自动将其引发。

    3. 用户定义(User_define) 错误

预定义异常一览:

处理异常:

代码语言:javascript
复制
select s.name into sname from z_student s where s.id=m;
  dbms_output.put_line('查询结果:' || sname);
  exception 
    when no_data_found then 
      dbms_output.put_line('无查询结果');
代码语言:javascript
复制
--预定义异常

DECLARE
   v_empno employees.employee_id%TYPE := &empno;
   v_sal   employees.salary%TYPE;
BEGIN
   SELECT salary INTO v_sal FROM employees WHERE employee_id = v_empno;
   IF v_sal<=1500 THEN 
        UPDATE employees SET salary = salary + 100 WHERE employee_id=v_empno; 
        DBMS_OUTPUT.PUT_LINE('编码为'||v_empno||'员工工资已更新!');     
   ELSE
        DBMS_OUTPUT.PUT_LINE('编码为'||v_empno||'员工工资已经超过规定值!');
   END IF;
EXCEPTION
   WHEN NO_DATA_FOUND THEN  
      DBMS_OUTPUT.PUT_LINE('数据库中没有编码为'||v_empno||'的员工');
   WHEN TOO_MANY_ROWS THEN
      DBMS_OUTPUT.PUT_LINE('程序运行错误!请使用游标');
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END; 

非预定义异常

对于这类异常情况的处理,首先必须对非定义的ORACLE错误进行定义。步骤如下:

1. 在PL/SQL 块的定义部分定义异常情况:

<异常情况>  EXCEPTION;

2. 将其定义好的异常情况,与标准的ORACLE错误联系起来,使用EXCEPTION_INIT语句:

PRAGMA EXCEPTION_INIT(<异常情况>, <错误代码>);

3. 在PL/SQL 块的异常情况处理部分对异常情况做出相应的处理。

代码语言:javascript
复制
--删除指定部门的记录信息,以确保该部门没有员工。
 
INSERT INTO departments VALUES(50, 'FINANCE', 'CHICAGO');

DECLARE
   v_deptno departments.department_id%TYPE := &deptno;
   deptno_remaining EXCEPTION;
   PRAGMA EXCEPTION_INIT(deptno_remaining, -2292);
   /* -2292 是违反一致性约束的错误代码 */
BEGIN
   DELETE FROM departments WHERE department_id = v_deptno;
EXCEPTION
   WHEN deptno_remaining THEN 
      DBMS_OUTPUT.PUT_LINE('违反数据完整性约束!');
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END;

自定义异常

步骤如下:

1. 在PL/SQL 块的定义部分定义异常情况:

<异常情况>  EXCEPTION;

2. RAISE <异常情况>;

3. 在PL/SQL 块的异常情况处理部分对异常情况做出相应的处理。

代码语言:javascript
复制
  if m = 0 then
    raise nozero;
  end if;

exception
  when nozero then
    dbms_output.put_line('m不能为0');
代码语言:javascript
复制
--更新指定员工工资,增加100;
 
DECLARE
   v_empno employees.employee_id%TYPE :=&empno;
   no_result  EXCEPTION;
BEGIN
   UPDATE employees SET salary = salary+100 WHERE employee_id = v_empno;
   IF SQL%NOTFOUND THEN
      RAISE no_result;
   END IF;
EXCEPTION
   WHEN no_result THEN 
      DBMS_OUTPUT.PUT_LINE('你的数据更新语句失败了!');
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END;
代码语言:javascript
复制
--创建一个函数get_salary, 该函数检索指定部门的工资总和,其中定义了-20991和-20992号错误,分别处理参数为空和非法部门代码两种错误:
 
CREATE TABLE errlog(
  Errcode NUMBER,
  Errtext CHAR(40));

CREATE OR REPLACE FUNCTION get_salary(p_deptno NUMBER)
RETURN NUMBER 
AS
  v_sal NUMBER;
BEGIN
  IF p_deptno IS NULL THEN
    RAISE_APPLICATION_ERROR(-20991, ’部门代码为空’);
  ELSIF p_deptno<0 THEN
    RAISE_APPLICATION_ERROR(-20992, ’无效的部门代码’);
  ELSE
    SELECT SUM(employees.salary) INTO v_sal FROM employees 
    WHERE employees.department_id=p_deptno;
    RETURN v_sal;
  END IF;
END;
DECLARE 
  V_salary NUMBER(7,2);
  V_sqlcode NUMBER;
  V_sqlerr VARCHAR2(512);
  Null_deptno EXCEPTION;
  Invalid_deptno EXCEPTION;
  PRAGMA EXCEPTION_INIT(null_deptno,-20991);
  PRAGMA EXCEPTION_INIT(invalid_deptno, -20992);
BEGIN
  V_salary :=get_salary(10);
  DBMS_OUTPUT.PUT_LINE('10号部门工资:' || TO_CHAR(V_salary));

  BEGIN
    V_salary :=get_salary(-10);
  EXCEPTION
    WHEN invalid_deptno THEN
      V_sqlcode :=SQLCODE;
      V_sqlerr  :=SQLERRM;
      INSERT INTO errlog(errcode, errtext) 
      VALUES(v_sqlcode, v_sqlerr);
      COMMIT;
  END inner1;

  V_salary :=get_salary(20);
  DBMS_OUTPUT.PUT_LINE('部门号为20的工资为:'||TO_CHAR(V_salary));

  BEGIN
    V_salary :=get_salary(NULL);
  END inner2;

  V_salary := get_salary(30);
  DBMS_OUTPUT.PUT_LINE('部门号为30的工资为:'||TO_CHAR(V_salary));

  EXCEPTION
    WHEN null_deptno THEN
      V_sqlcode :=SQLCODE;
      V_sqlerr  :=SQLERRM;
      INSERT INTO errlog(errcode, errtext) VALUES(v_sqlcode, v_sqlerr);
      COMMIT;
    WHEN OTHERS THEN
         DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END outer;
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-08-16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SQL和PL/SQL:
  • PL/SQL块
  • 变量 常量
    • 标量类型变量:
      • 引用型变量:
        • 记录型变量:
          • 使用%ROWTYPE声明记录类型数据
      • 数组类型
      • 结构控制:
        • if条件控制:
          • case语句
          • 循环:
          • 异常
            • 处理异常:
            相关产品与服务
            数据库
            云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档