PL/SQL Learning Section 2

Posted by airwinx on Mon, 15 Jul 2019 20:22:09 +0200

Conllection Type

PL/SQL has Collection Type: associative array] [1], VARRAY [(variable-size array)] [1], and nested table. All three declarations must be initialized, VARRAY and Nestes Table have constructors, and associative arraty [1] has no constructors.

[[VARRAY] [1]: You can modify and initialize arrays with constructors or access them with subscripts:

set serveroutput on;

DECLARE
TYPE People IS VARRAY(10) OF VARCHAR2(12);
Stu People := People('Yang','Wang','Zhang','Li');
PROCEDURE print_item(
    parm_head VARCHAR2)
IS
BEGIN
  dbms_output.put_line(parm_head);
  FOR i IN Stu.FIRST .. Stu.LAST
  LOOP
    dbms_output.put_line(i ||' : '|| Stu(i));
  END LOOP;
   dbms_output.put_line('________________');
END;

BEGIN
  print_item('First:"');
  Stu(2) := 'kai';
  print_item('Seccend:');
  Stu := People('Wu','Gu','Text','helo');
  print_item('Three:');
end;

In the example above, it is also true to replace VARRAY with TABLE (nested table) when defining a type.
While can also be used in loops, but a little change is needed if access is done through the methods he provides internally:

SET serveroutput ON;
DECLARE
TYPE People IS VARRAY(10) OF VARCHAR2(12);
Stu People    := People('Yang','Wang','Zhang','Li');
i PLS_INTEGER := Stu.FIRST;
PROCEDURE print_item(
    parm_head VARCHAR2)
IS
BEGIN
  dbms_output.put_line(parm_head);
  WHILE i IS NOT NULL
  LOOP
    dbms_output.put_line(i ||' : '|| Stu(i));
    i := Stu.NEXT(i);
  END LOOP;
  dbms_output.put_line('________________');
END;
BEGIN
  print_item('First:"');
  i      := Stu.FIRST();
  Stu(2) := 'kai';
  print_item('Seccend:');
  i   := Stu.FIRST();
  Stu := People('Wu','Gu','Text','helo');
  print_item('Three:');
END;

TABLE and VARRAY are discrete in memory allocation, that is, subscript values can be spaced such as Stu (1), Stu (3)... Wait. [VARRAY is fixed size, Table can dynamically grow its length] [3]

Table has another usage (I understand it as another usage) [associative array] [1]:

Dynamic arrays, which do not have constructors, have a value if the definition needs to be initialized manually:

DECLARE
TYPE People
IS
  TABLE OF VARCHAR2(23) INDEX BY PLS_INTEGER;
  Stu People;
  i PLS_INTEGER := Stu.FIRST;
PROCEDURE print_item(
    parm_head VARCHAR2)
IS
BEGIN
  dbms_output.put_line(parm_head);
  WHILE i IS NOT NULL
  LOOP
    dbms_output.put_line(i ||' : '|| Stu(i));
    i := Stu.NEXT(i);
  END LOOP;
  dbms_output.put_line('________________');
END;
BEGIN
  Stu(1) := 'Yang';
  Stu(2) := 'Wang';
  Stu(3) := 'Xhang';
  Stu(4) := 'Li';
  print_item('First:"');
  i      := Stu.FIRST();
  Stu(2) := 'kai';
  print_item('Seccend:');
  i := Stu.FIRST();
  --  Stu := People('Wu','Gu','Text','helo'); --No constructor
  print_item('Three:');
END;

A good way to initialize AssociatIve array is to initialize it with a function, which can be done in a package.

Package:

--*Baotou*
CREATE OR REPLACE PACKAGE My_Types IS
  TYPE My_AA IS TABLE OF VARCHAR(20) INDEX BY PLS_INTEGER;
  FUNCTION Init_My_AA RETURN My_AA;
END My_Types;

In this package, an associative array My_AA and a function for initialization are defined.

Enclaves

CREATE OR REPLACE PACKAGE BODY My_Types IS

  FUNCTION Init_My_AA RETURN My_AA IS
    ret My_AA;
  BEGIN
    ret(1) := 'HOW';
    ret(2) := 'ARE';
    ret(3) := 'YOU';
    RETURN ret;

  END Init_My_AA;
END My_Types;

Program package, the name of the package must be the same as that declared in the package header. The initialization of array My_AA is realized in the package.

Test call

 DECLARE
  v My_Types.My_AA := My_Types.Init_My_AA();

  Idx PLS_INTEGER := v.FIRST();

  PROCEDURE print_my_aa(my_ar My_Types.My_AA) IS
  BEGIN
    IF my_ar.COUNT() = 0 THEN
      dbms_output.put_line('empty');
      RETURN;
    END IF;
    FOR i IN my_ar.FIRST() .. my_ar.COUNT() LOOP
      dbms_output.put_line(my_ar(i));
    END LOOP;
  END print_my_aa;

BEGIN
  print_my_aa(v);
  dbms_output.put_line(' Counts : ' || v.Count());
END;

Create an implicit code block to call, [Note] [2] When declaring, use the package name. Variable name format, as well as when calling the initialization function. In the example above, a stored procedure is used to print the results.

When assignment statements are used to assign values, a set can also be assigned to an existing set. But if you have the same data type, you can't have the same element type.

DECLARE
  TYPE pro IS VARRAY(6) OF VARCHAR2(12);
  TYPE pro_1 IS VARRAY(6) oF VARCHAR2(12);

  group1 pro := pro('how', 'are', 'you');
  group2 pro;

  group3 pro_1 := pro_1('Just', 'So', 'So');
BEGIN
  group2 := group1;
  group2 := group3;
END;
ORA-06550: The first 11 That's ok, The first 13 column: 
PLS-00382: Expression type error
ORA-06550: The first 11 That's ok, The first 3 column: 
PL/SQL: Statement ignored

Above, the group2 and group3 element types are the same, but the data types redefined by type are different, resulting in exceptions.

Variables of type Conllection can also declare a called array (two-dimensional array):

DECLARE
  TYPE t1 IS VARRAY(10) OF VARCHAR2(23);
  TYPE nt1 IS VARRAY(10) OF t1;

  va  t1 := t1('hello', 'where', 'are', 'you', 'from');
  nva nt1 := nt1(va,
                 t1('I`m', 'KanKan', 'What`s'),
                 t1('Today', 'Before', 'Yesterday'),
                 va);

  PROCEDURE print_nt1(parm_nt1 nt1) IS
  BEGIN
    IF parm_nt1 IS NULL THEN
      dbms_output.put_line('NULL');
      RETURN;

    ELSIF parm_nt1.COUNT() = 0 THEN
      dbms_output.put_line('empty');
      RETURN;

    ELSE
      FOR i IN nva.FIRST() .. nva.COUNT() LOOP
        FOR j IN nva(i).FIRST() .. nva(i).COUNT() LOOP
          dbms_output.put('   ' || nva(i) (j) || '  ');
        END LOOP;
        dbms_output.put_Line('');

      END LOOP;
    END IF;

  END print_nt1;
BEGIN
  dbms_output.put_line('-------------------START BEFORE--------------------------');
  print_nt1(nva);
  dbms_output.put_line('--------------------EDN AFTER----------------------------');
  nva.EXTEND();
  nva(nva.COUNT) := va;
  print_nt1(nva);
  dbms_output.put_line(nva.COUNT);
END;

Create a two-dimensional array with access similar to C. If you need to expand, you can use the keyword [EXTEND][1], such as after EXTEND above, the length of the array is + 1.

Stored procedures, cursors, Nest Table

Demonstrate with Implicit Code Blocks:

Familiar with grammar:

DECLARE
  Idx PLS_INTEGER := 0;  
  --Definition record
TYPE re_emp
IS
  RECORD
  (
    t_name EMP.ENAME%TYPE ,
    t_mgr EMP.MGR%TYPE);  --  Define a record Used to store cursor values
  --Definition table
type tb_emp
IS
  TABLE OF re_emp;
  -- Define variables
  emps tb_emp;  -- Definition tableļ¼Œ Keep multiple records
  --cursor
  CURSOR cur(num NUMBER)
  IS
    SELECT ename ,MGR FROM emp WHERE sal < num;   -- Define a cursor to extract qualified records from the table
  --stored procedure
PROCEDURE pro_search_emps(
    t_num IN NUMBER,
    t_es OUT tb_emp )
IS
  es re_emp;   -- Define temporary record 
BEGIN
  dbms_output.put_line('______ procedure begin _____');
  dbms_output.put_line('t_num : '|| t_num);
  t_es := tb_emp();  -- Initialization table  Otherwise there will be Exception
  FOR es IN cur(t_num) -- to Table Add data (1)
  LOOP
    t_es.EXTEND();  -- again Table Add one at the end NULL value
    Idx       := Idx +1;
    t_es(Idx) := es;
  END LOOP;
END pro_search_emps;  --Stored procedures are defined to Table Add data to
BEGIN
  pro_search_emps(13354,emps);
  IF emps     IS NOT NULL THEN
    Idx       := emps.FIRST;
    WHILE Idx IS NOT NULL
    LOOP
      dbms_output.put_line(emps(Idx).t_name);
      Idx := emps.NEXT(Idx);
    END LOOP;
  ELSE
    dbms_output.put_line('emps  is Empty');
  END IF;
END;

Purpose:
Define a Record to store data taken from Cursor. A Table is defined to store multiple records.
Be careful:
- You must initialize variables before you use them. Define variables as much as possible before you define types.

If you change the for loop in Demo to white mode, the code is as follows:

    OPEN cur(t_num); 
    FETCH cur INTO es;
    IF cur%notfound THEN
      dbms_output.put_line('No information');
      RETURN;
    ELSE
          WHILE cur%found
          LOOP
            t_es.EXTEND();
            Idx       := Idx +1;
            t_es(Idx) := es;
            FETCH cur INTO es;
          END LOOP;
    END IF;
    CLOSE cur;

Replace the for <var> in <Cursor> loop <statement> end loop with the above while mode. Privately thought: while mode is easier to understand, but for is shorter.

Mark's done...

Topics: SQL Stored Procedure