• Oracle 11g Release 1 (11.1) PL/SQL_理解 Record 类型


    http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/collections.htm#i20479

    本文内容

    • 定义和声明 Record
    • Record 作为子程序参数和函数返回值
    • 给 Record 赋值
    • 比较 Record
    • 把 Record 插入到数据库
    • 把 Record 更新到数据库
    • Record 插入和更新的约束
    • 把查询数据放到 Record

    定义和声明 Record

    若创建 records,需要先定义一个 RECORD 类型,再用该类型声明变量。也可以创建或查找一个表、视图,或 PL/SQL 游标,总之是你想要的值,使用 %ROWTYPE 属性来创建匹配的 Record

    你可以在任何 PL/SQL 块、子程序或包的声明部分定义 RECORD 类型。当你自定义 RECORD 类型时,不能在域上指定一个 NOT NULL 的约束,或给出它们的默认值。

    示例 1:演示声明和初始化一个简单的 Record 类型

    DECLARE
       TYPE DeptRecTyp IS RECORD (
          deptid NUMBER(4) NOT NULL := 99,
          dname  departments.department_name%TYPE,
          loc    departments.location_id%TYPE,
          region regions%ROWTYPE );
       dept_rec DeptRecTyp;
    BEGIN
       dept_rec.dname := 'PURCHASING';
    END;
    /

    示例 2:演示声明和初始化 Record 类型

    DECLARE
    -- Declare a record type with 3 fields.
      TYPE rec1_t IS RECORD
        (field1 VARCHAR2(16), field2 NUMBER, field3 DATE);
    -- For any fields declared NOT NULL, you must supply a default value.
      TYPE rec2_t IS RECORD (id INTEGER NOT NULL := -1, 
      name VARCHAR2(64) NOT NULL := '[anonymous]');
    -- Declare record variables of the types declared
      rec1 rec1_t;
      rec2 rec2_t;
    -- Declare a record variable that can hold
    -- a row from the EMPLOYEES table.
    -- The fields of the record automatically match the names and
    -- types of the columns.
    -- Don't need a TYPE declaration in this case.
      rec3 employees%ROWTYPE;
    -- Or mix fields that are table columns with user-defined fields.
      TYPE rec4_t IS RECORD (first_name employees.first_name%TYPE,
                             last_name employees.last_name%TYPE,
                             rating NUMBER);
      rec4 rec4_t;
    BEGIN
    -- Read and write fields using dot notation
      rec1.field1 := 'Yesterday';
      rec1.field2 := 65;
      rec1.field3 := TRUNC(SYSDATE-1);
    -- Didn't fill name field, so it takes default value
      DBMS_OUTPUT.PUT_LINE(rec2.name);
    END;
    /

    若在数据库存储一个 Record,你可以在 INSERTUPDATE 语句指定,只要它的域与表的列匹配。

    你可以使用 %TYPE 来指定 Record 域类型对应表的列的类型。即使列类型改变了,你的代码仍然可以运行。例如,增加了 VARCHAR2 字段的长度,或 NUMBER 字段的精度。

    示例 3:演示使用 %ROWTYPE 来声明一个 Record,来保存 department 表的信息

    DECLARE
    -- Best: use %ROWTYPE instead of specifying each column.
    -- Use <cursor>%ROWTYPE instead of <table>%ROWTYPE because 
    -- you only want some columns.
    -- Declaring cursor doesn't run query or affect performance.
       CURSOR c1 IS
         SELECT department_id, department_name, location_id
         FROM departments;
       rec1 c1%ROWTYPE;
    -- Use <column>%TYPE in field declarations to avoid problems if 
    -- the column types change.
       TYPE DeptRec2 IS RECORD
         (dept_id   departments.department_id%TYPE,
          dept_name departments.department_name%TYPE,
          dept_loc  departments.location_id%TYPE);
       rec2 DeptRec2;
    -- Write each field name, specifying type directly
    -- (clumsy and unmaintainable for working with table data
    -- use only for all-PL/SQL code).
       TYPE DeptRec3 IS RECORD (dept_id NUMBER,
                                dept_name VARCHAR2(14),
                                dept_loc VARCHAR2(13));
       rec3 DeptRec3;
    BEGIN
       NULL;
    END;
    /

    PL/SQL 可以定义包含对象、集合和其他 Record(内置 Record)的 Record。但 Record 不能是对象类型的属性。

    若声明一个 Record,表示数据库表的一行,则无需列出列,使用 %ROWTYPE 属性。

    当表添加列后,你的代码仍然可以运行。若你想表示一个表列的一个子集,或是不同表的列,则你可以定义一个视图或声明一个游标,来选择右边的列,执行任何需要的连接,再在视图或游标上应用 %ROWTYPE

    Record 作为子程序参数和函数返回值

    Record 很容易用子程序来处理,因为,你只需传递一个 Record 参数,而不是 Record 每个单独的域。例如,你可以从 EMPLOYEES 表获得一行到一个 Record,再把这个行作为参数传递给一个函数,计算该员工的假期津贴。这个函数通过 Record 域,可以访问员工的所有信息。

    示例 4:演示从函数返回一个 Record

    若使 Record 类型对多个存储子程序可见,则在包规范中声明 Record 类型。

    DECLARE
       TYPE EmpRecTyp IS RECORD (
         emp_id       NUMBER(6),
         salary       NUMBER(8,2));
       CURSOR desc_salary RETURN EmpRecTyp IS
          SELECT employee_id, salary
          FROM employees
          ORDER BY salary DESC;
       emp_rec     EmpRecTyp;
       FUNCTION nth_highest_salary (n INTEGER) RETURN EmpRecTyp IS
       BEGIN
          OPEN desc_salary;
          FOR i IN 1..n LOOP
             FETCH desc_salary INTO emp_rec;
          END LOOP;
          CLOSE desc_salary;
          RETURN emp_rec;
       END nth_highest_salary;
    BEGIN
       NULL;
    END;
    /

    示例 5:演示 Record 作为存储过程的参数

    DECLARE
       TYPE EmpRecTyp IS RECORD (
          emp_id       NUMBER(6),
          emp_sal      NUMBER(8,2) );
       PROCEDURE raise_salary (emp_info EmpRecTyp) IS
       BEGIN
          UPDATE employees SET salary = salary + salary * .10
                 WHERE employee_id = emp_info.emp_id;
       END raise_salary;
    BEGIN
       NULL;
    END;
    /

    示例 6:演示声明一个 nested Record,一个 Record 由另一个 Record 组成

    DECLARE
       TYPE TimeTyp IS RECORD ( minutes SMALLINT, hours SMALLINT );
       TYPE MeetingTyp IS RECORD (
          day     DATE,
          time_of TimeTyp,             -- nested record
          dept    departments%ROWTYPE,
            -- nested record representing a table row
          place   VARCHAR2(20),
          purpose VARCHAR2(50) );
       meeting MeetingTyp;
       seminar MeetingTyp;
    BEGIN
    -- Can assign one nested record to another
    -- if they are of the same data type
       seminar.time_of := meeting.time_of;
    END;
    /

    即使 Record 有不同的数据类型,这样的赋值也是允许的。

    给 Record 赋值

    若为一个 Record 的所有域设置默认值, 则赋值为一个相同类型的、未初始化的 Record 给它。

    示例 7:演示为一个 Record 设置默认值

    DECLARE
       TYPE RecordTyp IS RECORD (field1 NUMBER, 
                                 field2 VARCHAR2(32) DEFAULT 'something');
       rec1 RecordTyp;
       rec2 RecordTyp;
    BEGIN
    -- At first, rec1 has the values you assign.
       rec1.field1 := 100; rec1.field2 := 'something else';
    -- Assigning an empty record to rec1
    -- resets fields to their default values.
    -- Field1 is NULL and field2 is 'something'
    -- due to the DEFAULT clause
       rec1 := rec2;
       DBMS_OUTPUT.PUT_LINE
         ('Field1 = ' || NVL(TO_CHAR(rec1.field1),'<NULL>') || ',
          field2 = ' || rec1.field2);
    END;
    /

    可以用点记号为一个 Record 的其中一个域赋值:

    emp_info.last_name := 'Fields';

    值被单独赋给一个 Record 的每个域。不能使用值的列表为一个 Record 赋值。

    只要两个 Record 类型相同,就可以赋值,这样一次性为 Record 每个域赋值。

    示例 8:演示用一个语句为一个 Record 的每个域赋值

    DECLARE
    -- Two identical type declarations.
       TYPE DeptRec1 IS RECORD
         (dept_num  NUMBER(2), dept_name VARCHAR2(14));
       TYPE DeptRec2 IS RECORD
        (dept_num  NUMBER(2), dept_name VARCHAR2(14));
       dept1_info DeptRec1;
       dept2_info DeptRec2;
       dept3_info DeptRec2;
    BEGIN
    -- Not allowed; different data types,
    -- even though fields are the same.
    --      dept1_info := dept2_info; 
    -- This assignment is OK because the records have the same type.
       dept2_info := dept3_info;
    END;
    /

    若用户自定义一个 Record%ROWTYPE record 域的个数、顺序和类型都相同,则可以把一个 %ROWTYPE Record 赋值给自定义的 Record

    DECLARE
       TYPE RecordTyp IS RECORD (last employees.last_name%TYPE, 
                                 id employees.employee_id%TYPE);
       CURSOR c1 IS SELECT last_name, employee_id FROM employees;
    -- Rec1 and rec2 have different types,
    -- but because rec2 is based on a %ROWTYPE,
    -- you can assign it to rec1 as long as they have
    -- the right number of fields and
    -- the fields have the right data types.
       rec1 RecordTyp;
       rec2 c1%ROWTYPE;
    BEGIN
      SELECT last_name, employee_id INTO rec2
        FROM employees WHERE ROWNUM < 2;
        WHERE ROWNUM < 2;
      rec1 := rec2;
      DBMS_OUTPUT.PUT_LINE
        ('Employee #' || rec1.id || ' = ' || rec1.last);
    END;
    /

    也可以使用 SELECTFETCH 语句,把列值获取到一个 Recordselect 列出的字段顺序必须与 Record 相同。

    示例 9:演示使用 SELECT INTO 为一个 Record 赋值

    DECLARE
       TYPE RecordTyp IS RECORD (last employees.last_name%TYPE, 
                                 id employees.employee_id%TYPE);
       rec1 RecordTyp;
    BEGIN
      SELECT last_name, employee_id INTO rec1
         FROM employees WHERE ROWNUM < 2;
         WHERE ROWNUM < 2;
       DBMS_OUTPUT.PUT_LINE
         ('Employee #' || rec1.id || ' = ' || rec1.last);
    END;
    /

    比较 Record

    Record 不能用 NULL 来测试,不能用等号或不等号比较。若想比较,则需要自定义函数。

    把 Record 插入到数据库

    VALUES 子句,使用一个类型为 RECORD%ROWTYPE 的变量,而不是列出 Record 的每个域,通过 PL/SQL INSERT 语句扩展可以将多个 Record 插入到数据库。这可以使代码更可读,更容易维护。

    若通过 FORALL 语句执行 INSERT,则可以从 record 整个集合插入值。record 域的数量必须等于 INTO 子句列出的列数量,且域和列必须类型兼容。若确保 record 与 table 兼容,最方便的方法用 table_name%ROWTYPE 声明变量。

    示例 10:演示通过 %ROWTYPE 标识符插入一个 Record

    该方法无需指定列。%ROWTYPE 确保 Record 域与表列的名称和类型完全相同。

    DECLARE
      dept_info departments%ROWTYPE;
    BEGIN
      -- department_id, department_name, and location_id
      -- are the table columns
      -- The record picks up these names from the %ROWTYPE
         dept_info.department_id := 300;
         dept_info.department_name := 'Personnel';
         dept_info.location_id := 1700;
      -- Using the %ROWTYPE means you can leave out the column list
      -- (department_id, department_name, and location_id)
      -- from the INSERT statement
      INSERT INTO departments VALUES dept_info;
    END;
    /

    把 Record 更新到数据库

    SET 子句右边使用一个类型为 RECORD%ROWTYPE 的单独变量,UPDATE 语句的 PL/SQL 扩展可以更新数据的行。

    若通过 FORALL 语句执行 UPDATE,则可以从 record 整个集合更新值。另外,对于一个 UPDATE 语句,可以在 RETURNING 子句指定一个 Record,检索新数据到一个 Record,Record 的集合。

    record 域的数量必须等于 SET 子句列出的列数量,且域和列必须类型兼容。

    可以使用关键 ROW 来表示整个行。

    示例 11:演示用一个 Record 更新一行

    DECLARE
       dept_info departments%ROWTYPE;
    BEGIN
    -- department_id, department_name, and location_id
    -- are the table columns
    -- The record picks up these names from the %ROWTYPE.
      dept_info.department_id := 300;
      dept_info.department_name := 'Personnel';
      dept_info.location_id := 1700;
    -- The fields of a %ROWTYPE
    -- can completely replace the table columns
    -- The row will have values for the filled-in columns, and null
    -- for any other columns
       UPDATE departments SET ROW = dept_info WHERE department_id = 300;
    END;
    /

    关键字 ROW 只能用在 SET 子句的左边。SET ROW 的参数必须是一个真正的 PL/SQL Record,而不能是只返回一行的子查询。Record 也可以包含集合或对象。

    INSERTUPDATEDELETE 语句可以包含 RETURNING 子句,返回受影响的行的列值到 PL/SQL Record 变量。这消除了插入或更新后,或删除前,重新 select 数据的需求。

    默认情况下,当只操作一个行数据时,可以使用该子句。当你使用 bulk SQL 时,你可以使用 RETURNING BULK COLLECT INTO 把结果存储到一个或多个集合中。

    示例 12:演示使用 RETURNING INTO 子句

    更新指定员工的工资,并检索该员工的名字、职位和新的工资到一个 Record 变量。

    DECLARE
       TYPE EmpRec IS RECORD (last_name  employees.last_name%TYPE, 
                              salary     employees.salary%TYPE);
       emp_info EmpRec;
       emp_id   NUMBER := 100;
    BEGIN
       UPDATE employees SET salary = salary * 1.1
         WHERE employee_id = emp_id
         RETURNING last_name, salary INTO emp_info;
       DBMS_OUTPUT.PUT_LINE
         ('Just gave a raise to ' || emp_info.last_name ||
          ', who now makes ' || emp_info.salary);
       ROLLBACK;
    END;
    /

    把 Record 插入和更新的约束

    目前,下面约束用于 record inserts/updates:

    • Record 变量只能用在下面地方:
        1. UPDATE 语句中的 SET 子句的右边
        2. INSERT 语句的 VALUES 子句
        3. RETURNING 子句的 INTO 子句

    Record 变量不能用在 SELECT 列表、WHERE 子句、GROUP BY 子句,或 ORDER BY 子句。

    • 关键字 ROW 只能用在 SET 子句的左边。另外,不能使用带子查询的 ROW
    • UPDATE 语句,若使用 ROW,则只能用一个 SET 子句
    • INSERT 语句的 VALUES 子句包含一个 Record 变量,则不能再使用其他变量或值。
    • RETURNING 子句的 INTO 子句包含一个 Record 变量,则不能再使用其他变量或值。
    • 下面不支持:
        1. Nested record 类型
        2. 返回一个 Record 的函数
        3. 使用 EXECUTE IMMEDIATE 语句进行 Record 插入和更新

    把查询数据放到 Record

    可以使用带 BULK COLLECT 子句的 SELECT INTOFETCH 语句,来检索行集合到 Record。

    示例 13:演示使用带 BULK COLLECT 的 SELECT INTO 语句

    DECLARE
       TYPE EmployeeSet IS TABLE OF employees%ROWTYPE;
       underpaid EmployeeSet;
         -- Holds set of rows from EMPLOYEES table.
       CURSOR c1 IS SELECT first_name, last_name FROM employees;
       TYPE NameSet IS TABLE OF c1%ROWTYPE;
       some_names NameSet;
         -- Holds set of partial rows from EMPLOYEES table.
    BEGIN
    -- With one query,
    -- bring all relevant data into collection of records.
       SELECT * BULK COLLECT INTO underpaid FROM employees
          WHERE salary < 5000 ORDER BY salary DESC;
    -- Process data by examining collection or passing it to
    -- eparate procedure, instead of writing loop to FETCH each row.
       DBMS_OUTPUT.PUT_LINE
         (underpaid.COUNT || ' people make less than 5000.');
       FOR i IN underpaid.FIRST .. underpaid.LAST
       LOOP
         DBMS_OUTPUT.PUT_LINE
           (underpaid(i).last_name || ' makes ' || underpaid(i).salary);
       END LOOP;
    -- You can also bring in just some of the table columns.
    -- Here you get the first and last names of 10 arbitrary employees.
       SELECT first_name, last_name
         BULK COLLECT INTO some_names
         FROM employees
         WHERE ROWNUM < 11;
       FOR i IN some_names.FIRST .. some_names.LAST
       LOOP
          DBMS_OUTPUT.PUT_LINE
            ('Employee = ' || some_names(i).first_name
             || ' ' || some_names(i).last_name);
       END LOOP;
    END;
    /

  • 相关阅读:
    ubuntu切换中英文通用方法,ubuntu中文语言
    ubuntu安装ibus-goolepinyin通用方法
    ubuntu12.04 64位系统配置jdk1.6和jdk-6u20-linux-i586.bin下载地址
    ubuntu创建桌面快捷方式
    vim记住上次编辑和浏览位置
    ubuntu12.04安装tftp,配置,修改目录,错误类型
    Ubuntu 12.04 make menuconfig 出现 Unable to find the ncurses libraries or the required header files.
    nginx六 之Session共享
    nginx五 之高可用
    nginx四 之缓存模块
  • 原文地址:https://www.cnblogs.com/liuning8023/p/2498772.html
Copyright © 2020-2023  润新知