• PL/SQL程序设计


    第一章:PL/SQL概述:

    什么是PL/SQL:
    A。PL/SQL是 Procedure Language & Structured Query Language 的缩写。PL/SQL是对SQL语言存储过程语言的扩展。从ORACLE6以后,ORACLE的RDBMS附带了PL/SQL。它现在已经成为一种过程处理语言,简称PL/SQL。目前的PL/SQL包括两部分,一部分是数据库引擎部分;另一部分是可嵌入到许多产品(如C语言,JAVA语言等)工具中的独立引擎。可以将这两部分称为:数据库PL/SQL和工具PL/SQL。两者的编程非常相似。都具有编程结构、语法和逻辑机制。
    B。PL /SQL是一种高级数据库程序设计语言,该语言专门用于在各种环境下对ORACLE数据库进行访问。由于该语言集成于数据库服务器中  ,所以PL/SQL代码可以对数据进行快速高效的处理。

    PL/SQL的好处:
    A。有利于客户/服务器环境应用的运行
     对于客户/服务器环境来说,真正的瓶颈是网络上。无论网络多快,只要客户端与服务器进行大量的数据交换。应用运行的效率自然就回受到影响。如果使用PL/SQL进行编程,将这种具有大量数据处理的应用放在服务器端来执行。自然就省去了数据在网上的传输时间。 
    B。适合于客户环境  
     PL/SQL由于分为数据库PL/SQL部分和工具PL/SQL。对于客户端来说,PL/SQL可以嵌套到相应的工具中,客户端程序可以执行本地包含PL/SQL部分,也可以向服务发SQL命令或激活服务器端的PL/SQL程序运行。

    第二章:PL/SQL块结构与组成元素:

    A。PL/SQL块:
     PL/SQL程序由三个块组成,即声明部分、执行部分、异常处理部分。结构如下:
      DECLARE
      /* 声明部分: 在此声明PL/SQL用到的变量,类型及游标,以及局部的存储过程和函数 */
      BEGIN
      /* 执行部分: 过程及SQL 语句 , 即程序的主要部分 */
      EXCEPTION
      /* 执行异常部分: 错误处理 */
      END;
     其中执行部分是必须的。
     PL/SQL块可以分为三类:
      1. 无名块:动态构造,只能执行一次。
      2. 子程序:存储在数据库中的存储过程、函数及包等。当在数据库上建立好后可以在其它程序中调用它们。
      3. 触发器:当数据库发生操作时,会触发一些事件,从而自动执行相应的程序。
      
    B。标识符:
     PL/SQL程序设计中的标识符定义与SQL 的标识符定义的要求相同。要求和限制有:
     不能超过30字符(最好8个左右);
     字母开头,不分大小写;
     不能用’-‘(减号);
     不能是SQL保留字。
     提示:一般不要把变量名声明与表中字段名完全一样,这样可能得到不正确的结果.例如下面的例子将会删除所有的纪录。
     DECLARE
     ename varchar2(20) :=’KING’;
     BEGIN
     DELETE FROM emp WHERE ename=ename;
     END;
     
    C。声明变量
     语法:Var_name type [CONSTANT][NOT NULL][:=value];
     说明:赋值语句为“:=”;如有[CONSTANT][NOT NULL],变量一定要有一个初始值;规定没有初始化的变量为NULL。
     
    D。记录类型RECORD
     记录类型是把逻辑相关的数据作为一个单元存储起来,它必须包括至少一个标量型或RECORD 数据类型的成员,称作PL/SQL RECORD 的域(FIELD),其作用是存放互不相同但逻辑相关的信息。
     定义记录类型语法如下:
      TYPE record_name IS RECORD(        /*其中TYPE,IS,RECORD为关键字,record_name为变量名称*/
      field1 type [NOT NULL][:=expr1],   /*每个等价的成员间用逗号分隔*/
      field2 type [NOT NULL][:=expr2],   /*如果一个字段限定NOT NULL,那么它必须拥有一个初始值*/
              ...                        /*所有没有初始化的字段都会初始为NULL
      fieldn type [NOT NULL][:=exprn]);
     例如:
      DECLARE
      TYPE test_record IS RECORD(
      Code VARCHAR2(10),
      Name VARCHAR2(30) NOT NULL :=’a book’);
      V_book test_record;  /*类似实例化对象*/
      BEGIN
      V_book.code :=’123’;
      V_book.name :=’C++ Programming’;
      DBMS_OUTPUT.PUT_LINE(v_book.code||v_book.name);
      END;

    E。使用%TYPE
     定义一个变量,其数据类型与已经定义的某个数据变量的类型相同,或者与数据库表的某个列的数据类型相同,这时可以使用%TYPE。
     使用%TYPE特性的优点在于:
     1。所引用的数据库列的数据类型可以不必知道;
     2。所引用的数据库列的数据类型可以实时改变。
     例如:

    DECLARE
       TYPE t_Record IS RECORD(
              T_no emp.empno%TYPE,
              T_name emp.ename%TYPE);

    F。使用%ROWTYPE
     PL/SQL 提供%ROWTYPE操作符, 返回一个记录类型, 其数据类型和数据库表的数据结构相一致。
     使用%ROWTYPE特性的优点在于:
     1。所引用的数据库中列的个数和数据类型可以不必知道;
     2。所引用的数据库中列的个数和数据类型可以实时改变。
     例如:
     DECLARE
     v_empno emp.empno%TYPE :=&empno;
     rec emp%ROWTYPE;  /*rec的结构和表emp相同*/

     BEGIN
     SELECT * INTO rec FROM emp WHERE empno=v_empno;
     DBMS_OUTPUT.PUT_LINE('姓名:'||rec.ename||'工资:'||rec.sal||'工作时间:'||rec.hiredate);
     END;
     
    G。数据库赋值
     数据库赋值是通过 SELECT语句来完成的,每次执行 SELECT语句就赋值一次,一般要求被赋值的变量与SELECT中的列名要一一对应。
     例如:
      DECLARE
      emp_id emp.empno%TYPE :=7788;
      emp_name emp.ename%TYPE;
      wages emp.sal%TYPE;
      BEGIN
      SELECT ename, NVL(sal,0) + NVL(comm,0) INTO emp_name, wages 
      /*NVL(a,b)函数说明:a!=null,return a; a==null,return b; a==null&&b==null,return null*/
      FROM emp WHERE empno = emp_id;
      Dbms_output.put_line(emp_name||’----‘||to_char(wages));
      END;
     提示:不能将SELECT语句中的列赋值给布尔变量。  

    Code

    H。可转换的类型赋值
     1。CHAR 转换为 NUMBER:v_total := TO_NUMBER(‘100.0’) + sal;
     2。NUMBER 转换为CHAR:v_comm := TO_CHAR(‘123.45’) || ’元’ ;
     3。字符转换为日期:v_date := TO_DATE('2001.07.03','yyyy.mm.dd');
     4。日期转换为字符:v_to_day := TO_CHAR(SYSDATE, 'yyyy.mm.dd hh24:mi:ss');
     
    第三章:PL/SQL流程控制语句

    A。IF语句

    Code

    B。CASE语句:

    Code

    C。WHILE语句

    DECLARE
    x NUMBER;
    BEGIN
    x:
    = 1;
    WHILE x
    <10 LOOP
    DBMS_OUTPUT.PUT_LINE(
    'X的当前值为:'||x);
    x:
    = x+1;
    END LOOP;
    END;

    D。数字循环
     格式:
      FOR 循环计数器 IN [ REVERSE ] 下限 .. 上限 LOOP
      要执行的语句;
      END LOOP;
     说明:
      每循环一次,循环变量自动加1;使用关键字REVERSE,循环变量自动减1数字必须是从小到大的顺序,而且必须是整数,不能是变量或表达式。可以使用EXIT 退出循环。
     例子:
      BEGIN
      FOR int in 1..10 LOOP
      DBMS_OUTPUT.PUT_LINE('int 的当前值为: '||int);
      END LOOP;
      END;

    E。NULL语句
     在语句块中加空语句,用于补充语句的完整性,提高程序的可读性
     例如:
        IF boolean_expr THEN
        ...
        ELSE
          NULL;
        END IF;

    第四章:游标的使用

    游标是一个指向上下文的句柄( handle)或指针。通过游标,PL/SQL可以控制上下文区和处理语句时上下文区会发生些什么事情。用于提取多行数据集

    A。声明:
     格式:CURSOR cursor_name[(parameter[, parameter]…)] IS select_statement;,如:
     1。普通申明:DELCARE CURSOR CURSOR_NAME IS select_statement /* CURSOR的内容必须是一条查询语句*/
     2。带参数申明:DELCARE CURSOR c_stu(p_id student.ID%TYPE) IS SELECT * FROM student WHERE ID = p_id;
     提示:在指定数据类型时,可以使用%TYPE;不能使用长度约束。如NUMBER(4)、CHAR(10) 等都是错误的。

    B。打开游标:
     就是执行游标所对应的SELECT 语句,将其查询结果放入工作区,并且指针指向工作区的首部,标识游标结果集合。相当于执行select语句,且把执行结果存入CURSOR。如果游标查询语句中带有FOR UPDATE选项,OPEN 语句还将锁定数据库表中游标结果集合对应的数据行。
     格式:OPEN cursor_name[value[,value]....];PL/SQL 程序不能用OPEN 语句重复打开一个游标

    C。提取游标数据
     检索结果集合中的数据行,放入指定的输出变量中,每FETCH一次取一条记录
     格式:FETCH cursor_name INTO {var_list | record_var };  //var_list变量的数量、类型、顺序要和Table中字段一致。

    D。关闭游标:
     格式:CLOSE Cursor_name;
     说明:
        1. 游标使用后应该关闭;关闭后的游标可以使用OPEN 语句重新打开。
        2. 关闭后的游标不能FETCH和再次CLOSE。
        3. 关闭游标相当于将内存中CURSOR的内容清空

    E。FETCH的几种循环
      1。
      LOOP
       FETCH cursor INTO ...
       EXIT WHEN cursor%NOTFOUND;
      END LOOP;
        2。
        WHILE cursor%FOUND LOOP
          FETCH cursor INTO ...
        END LOOP;
        3。
        FOR var IN cursor LOOP
          FETCH cursor INTO...
        END LOOP;

    F。隐式游标属性
     %FOUND 布尔型属性,当最近一次读记录时成功返回,则值为true;
     %NOTFOUND 布尔型属性,与%found相反;
     %ROWCOUNT 数字型属性, 返回已从游标中读取得记录数;
     %ISOPEN 布尔型属性, 判断游标是否是打开状态。

    G:游标修改和删除操作
     为了对正在处理(查询)的行不被另外的用户改动,ORACLE 提供一个 FOR UPDATE 子句来对所选择的行进行锁住。该需求迫使ORACLE锁定游标结果集合的行,可以防止其他事务处理更新或删除相同的行,直到您的事务处理提交或回退为止。
     SELECT...FROM...FOR UPDATE [OF column[, column]...][NOWAIT]
     如果另一个会话已对活动集中的行加了锁,那么SELECT FOR UPDATE操作一直等待到其它的会话释放这些锁后才继续自己的操作,对于这种情况,当加上NOWAIT子句时,如果这些行真的被另一个会话锁定,则OPEN立即返回并给出提示
     ORA-0054 :resource busy and acquire with nowait specified.

    例子1:

    Code

    例子2:给工资低于1200 的员工增加工资50。

    Code

    例子3:从EMP表中查询某部门的员工情况,将其工资最低定为 1500。

    Code

     
    第五章:异常错误处理

    异常处理部分一般放在 PL/SQL 程序体的后半部,结构为:
     EXCEPTION
     WHEN first_exception THEN <code to handle first exception >
     WHEN second_exception THEN <code to handle second exception >
     WHEN OTHERS THEN <code to handle others exception >
     END;
    异常处理可以按任意次序排列,但 OTHERS 必须放在最后
     
    A。预定义的异常处理
     ORACLE预定义的异常情况大约有24个。对这种异常情况的处理,无需在程序中定义,由ORACLE自动将其引发。
     具体异常的名称:No_data_found,Timeout-on-resource,Program-error等等。
     EXCEPTION
     WHEN NO_DATA_FOUND THEN
     DBMS_OUTPUT.PUT_LINE('数据库中没有满足条件的数据');
     WHEN Invalid-CURSOR THEN
     DBMS_OUTPUT.PUT_LINE('试图使用一个无效的游标');
     WHEN OTHERS THEN
     DBMS_OUTPUT.PUT_LINE('发生其它错误!');
     END;
     
    B。非预定义的异常处理
     即其他标准的ORACLE错误。对这种异常情况的处理,需要用户在程序中定义,然后将其定义好的异常情况用EXCEPTION_INIT语句与标准的ORACLE错误联系起来,由ORACLE自动将其引发(变相的预定义异常)。

    C。用户自定义的异常处理
     DECLARE
     v_empno emp.empno%TYPE :=&empno;
     no_result EXCEPTION;  /*自定义异常*/
     BEGIN
     UPDATE emp SET sal=sal+100 WHERE empno=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('发生其它错误!');
     END;

    第六章:存储函数和过程
     过程和函数的唯一区别是函数总向调用者返回数据,而过程则不返回数据。没有返回语句的函数将是一个错误。
     在PL/SQL 程序中还可以在块内建立本地函数和过程,这些函数和过程不存储在数据库中,但可以在创建它们的PL/SQL 程序中被重复调用。本地函数和过程在PL/SQL 块的声明部分定义,它们的语法格式与存储函数和过程相同,但不能使用CREATE OR REPLACE 关键字。

    A。建立内嵌函数
     获取某部门的工资总和:

    Code

    B。内嵌函数的调用
     计算某部门的工资总和:
      DECLARE
      v_num NUMBER;
      v_sum NUMBER;
      BEGIN
      v_sum :=get_salary(30, v_num);  /*函数out v_num,返回 v_sum*/
      DBMS_OUTPUT.PUT_LINE(’30号部门工资总和:’||v_sum||’,人数:’||v_num);
      END;
     1。位置表示法:以上的参数传递称方式为位置表示法,要求参数顺序一定。
     2。名称表示法:没有要求参数传递的顺序,如:
      v_sum :=get_salary(emp_count => v_num, dept_no => 30);
     3。混合表示法:位置表示法所传递的参数必须放在名称表示法所传递的参数前面,即只要其中有一个参数使用名称表示法,其后所有的参数 都必须使用名称表示法。(这里用名称表示法传递的参数当然也不要求顺序)
     
    C。函数参数默认值

    Code

    D。创建存储过程:
     1。无参:
      CREATE table logtable (userid VARCHAR2(10), logdate date);
      CREATE OR REPLACE PROCEDURE logexecution IS
      BEGIN
      INSERT INTO logtable (userid, logdate) VALUES (USER, SYSDATE);
      END;
     2。带参。计算指定部门的工资总和,并统计其中的职工数量:
      CREATE OR REPLACE PROCEDURE proc_demo(
      Dept_no NUMBER DEFAULT 10,
      sal_sum OUT NUMBER,
      emp_count OUT NUMBER)
      IS
      BEGIN
      SELECT SUM(sal), COUNT(*) INTO sal_sum, emp_count  /*把值存进两个输出参数*/
      FROM emp WHERE deptno=dept_no;
      EXCEPTION
      WHEN NO_DATA_FOUND THEN
      DBMS_OUTPUT.PUT_LINE('你需要的数据不存在!');
      WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE('发生其它错误!');
      END proc_demo;

    E。调用存储过程:
     1。无参:
      EXEC[UTE] Procedure_name( parameter1, parameter2...);
     2。带参:
      DECLARE
      V_num NUMBER;
      V_sum NUMBER(8, 2);
      BEGIN
      Proc_demo(30, v_sum, v_num);
      DBMS_OUTPUT.PUT_LINE('30号部门工资总和:'||v_sum||’,人数:’||v_num);
      Proc_demo(sal_sum => v_sum, emp_count => v_num);
      DBMS_OUTPUT.PUT_LINE('10号部门工资总和:'||v_sum||’,人数:’||v_num);
      END;

    F。删除过程、函数
     1.删除过程:DROP PROCEDURE [user.]Procudure_name;
     2.删除函数:DROP FUNCTION [user.]Function_name;
     

    第七章:包

    A。包的概述:
     1。包是一组相关过程、函数、变量、常量和游标等PL/SQL程序设计元素的组合,它具有面向对象程序设计语言的特点,是对这些PL/SQL 程序设计元素的封装。包类似于C++和JAVA语言中的类,其中变量相当于类中的成员变量,过程和函数相当于类方法。把相关的模块归类成为包,可使开发人员利用面向对象的方法进行存储过程的开发,从而提高系统性能。
     2。与类相同,包中的程序元素也分为公用元素和私用元素两种,这两种元素的区别是他们允许访问的程序范围不同,即它们的作用域不同。公用元素不仅可以被包中的函数、过程所调用,也可以被包外的PL/SQL程序访问,而私有元素只能被包内的函数和过程序所访问。
     3。在PL/SQL程序设计中,使用包不仅可以使程序设计模块化,对外隐藏包内所使用的信息(通过使用私用变量),而写可以提高程序的执行效率。因为,当程序首次调用包内函数或过程时,ORACLE将整个包调入内存,当再次访问包内元素时,ORACLE直接从内存中读取,而不需要进行磁盘I/O操作,从而使程序执行效率得到提高。 

    B。包的组成:
     一个包由两个分开的部分组成:
     包定义(PACKAGE):声明包内数据类型、变量、常量、游标、子程序和异常错误处理等元素,这些元素为包的公有元素。
     包主体(PACKAGE BODY):是包定义部分的具体实现。它定义了包定义部分所声明的游标和子程序,在包主体中还可以声明包的私有元素。
     包定义和包主体分开编译,并作为两部分分开的对象存放在数据库字典中,详见数据字典user_source, all_source, dba_source.

    C。创建包定义:
     CREATE [OR REPLACE] PACKAGE package_name
     [AUTHID {CURRENT_USER | DEFINER}]
     {IS | AS}
     [公有数据类型定义[公有数据类型定义]…]
     [公有游标声明[公有游标声明]…]
     [公有变量、常量声明[公有变量、常量声明]…]
     [公有子程序声明[公有子程序声明]…]
     END [package_name];
     其中:AUTHID CURRENT_USER和AUTHID DEFINER选项说明应用程序在调用函数时所使用的权限模式。
     
    D。创建包主体:
     CREATE [OR REPLACE] PACKAGE BODY package_name
     {IS | AS}
     [私有数据类型定义[私有数据类型定义]…]
     [私有变量、常量声明[私有变量、常量声明]…]
     [私有子程序声明和定义[私有子程序声明和定义]…]
     [公有游标定义[公有游标定义]…]
     [公有子程序定义[公有子程序定义]…]
     BEGIN
     PL/SQL 语句
     END [package_name];
     其中:在包主体定义公有程序时,它们必须与包定义中所声明子程序的格式完全一致。

    E。包的调用、删除和重载:
     1。对包内共有元素的调用格式为:包名.元素名称
     2。删除包:DROP PACKAGE [BODY] [user.]package_name;
     3。PL/SQL 允许对包内子程序和本地子程序进行重载。所谓重载时指两个或多个子程序有相同的名称,但拥有不同的参数变量、参数顺序或参数数据类型。

     
    F。例子:
     1。创建的包为demo_pack, 该包中包含一个记录变量DeptRec、两个函数和一个过程。

    Code

      2。包主体的创建方法,它实现上面所声明的包定义,并在包主体中声明一个私有变量flag和一个私有函数check_dept,由于在add_dept和re move_dept等函数中需要调用check_dpet函数,所以,在定义check_dept 函数之前首先对该函数进行声明,这种声明方法称作前向声明。

    Code

       3。调用demo_pack包内函数对dept表进行插入、查询和修改操作,并通过demo_pack包中的记录变量DeptRec 显示所查询到的数据库信息:

    Code

    第八章:触发器
     
    A。概述:
     1。触发器在数据库里以独立的对象存储,它与存储过程不同的是,存储过程通过其它程序来启动运行或直接启动运行,而触发器是由一个事件来启动运行。即触发器是当某个事件发生时自动地隐式运行。并且,触发器不能接收参数
     2。ORACLE事件指的是对数据库的表进行的INSERT、UPDATE及DELETE操作或对视图进行类似的操作。ORACLE将触发器的功能扩展到了触发ORACLE,如数据库的启动与关闭等
     
    B。触发器组成:
     触发事件:即在何种情况下触发TRIGGER; 例如:INSERT, UPDATE, DELETE。
     触发时间:即该TRIGGER 是在触发事件发生之前(BEFORE)还是之后(AFTER)触发,也就是触发事件和该TRIGGER 的操作顺序。
     触发器本身:即该TRIGGER 被触发之后的目的和意图,正是触发器本身要做的事情。 例如:PL/SQL 块。
     触发频率:说明触发器内定义的动作被执行的次数。即语句级(STATEMENT)触发器和行级(ROW)触发器
      1。语句级(STATEMENT)触发器:是指当某触发事件发生时,该触发器只执行一次;
      2。行级(ROW)触发器:是指当某触发事件发生时,对受到该操作影响的每一行数据,触发器都单独执行一次。

    C。创建触发器:
     格式:
      CREATE [OR REPLACE] TRIGGER trigger_name
      {BEFORE | AFTER | INSTEAD OF}
      {INSERT | DELETE | UPDATE [OF column [, column …]]}
      ON {[schema.] table_name | [schema.] view_name}
      [REFERENCING {OLD [AS] old | NEW [AS] new| PARENT as parent}]
      [FOR EACH ROW ]
      [WHEN condition]
      trigger_body;
     说明:
      1。BEFORE 和AFTER指出触发器的触发时序分别为前触发和后触发方式,前触发是在执行触发事件之前触发当前所创建的触发器,后触发是在执行触发事件之后触发当前所创建的触发器。
      2。INSTEAD OF 选项使ORACLE激活触发器,而不执行触发事件。只能对视图和对象视图建立INSTEAD OF触发器,而不能对表、模式和数据库建立INSTEAD OF 触发器。
      3。FOR EACH ROW选项说明触发器为行触发器。行触发器和语句触发器的区别表现在:行触发器要求当一个DML语句操走影响数据库中的多行数据时,对于其中的每个数据行,只要它们符合触发约束条件,均激活一次触发器;而语句触发器将整个语句操作作为触发事件,当它符合约束条件时,激活一次触发器。当省略FOR EACH ROW 选项时,BEFORE 和AFTER 触发器为语句触发器,而INSTEAD OF 触发器则为行触发器。
      4。WHEN 子句指定的触发约束条件只能用在BEFORE 和AFTER 行触发器中,不能用在INSTEAD OF 行触发器和其它类型的触发器中。
     
    D。触发器的限制:
     1。触发器中不能使用数据库事务控制语句 COMMIT; ROLLBACK, SVAEPOINT 语句;
     2。由触发器所调用的过程或函数也不能使用数据库事务控制语句;
     3。触发器中不能使用LONG, LONG RAW 类型
     4。触发器的主体不可以读取或修改任何变化表(被DML语句正在修改的表);
     5。触发器的主体不可以读取或修改限制表(带有约束的表)的主键、唯一值、外键列

     问题:当触发器被触发时,有时要使用被插入、更新或删除的记录中的列值,有时要使用操作前、 后列的值,如何区分。
     实现:
     :new 修饰符访问操作完成后列的值
     :old 修饰符访问操作完成前列的值

    E。例子:
     1。建立一个触发器, 当职工表 emp 表被删除一条记录时,把被删除记录写到职工表删除日志表中去

    Code

     2。创建Instead_of触发器
      问题:
       CREATE OR REPLACE VIEW emp_view AS
       SELECT deptno, count(*) total_employeer, sum(sal) total_salary
       FROM emp GROUP BY deptno;
       /*直接操作会引起异常*/
       DELETE FROM emp_view WHERE deptno=10;  /*ORA-01732: 此视图的数据操纵操作非法*/
      实现:

    Code
  • 相关阅读:
    大航海计划
    副业刚需
    【转】iOS学习之适配iOS10
    【原】iOS学习之Masonry第三方约束
    【原】iOS学习之PINCache第三方缓存框架
    【原】iOS学习之苹果原生代码实现Autolayout和VFL语言
    【原】iOS学习之NSDate在项目中的一些类目扩展
    【原】iOS学习之tableView的常见BUG
    【转】iOS开发 -- Apple Pay
    【原】iOS学习之事件处理的原理
  • 原文地址:https://www.cnblogs.com/vipcjob/p/1546880.html
Copyright © 2020-2023  润新知