sábado, 17 de septiembre de 2016

Tipos de Datos Compuestos.

Objetivos:
• Describir los records y collections de PL/SQL.
• Crear records definidos por el usuario.
• Crear un record con el atributo %ROWTYPE.
• Crear matrices asociativas(associative arrays):
--INDEX BY table.
--INDEX BY table of records.
NOTA: Usamos como ejemplo la Base de Datos: ORCL, la cual viene por defecto en cualquier versión de ORACLE.
____________________________________________________________________________________
Tipos de Datos Compuestos.
Las variables escalares pueden contener sólo un valor, mientras que las variables de tipo de dato compuesto pueden contener varios valores de tipo de dato escalar o el tipo de dato compuesto. Los dos tipos de tipos de datos compuestos disponibles en PL/SQL son:

• PL/SQL Records: Los Records se utilizan para tratar como una unidad lógica tipos de datos distintos que guardan relación. Un Records de PL/SQL puede tener variables de diferentes tipos. Por ejemplo, puede definir un Record para contener detalles de empleados. Esto implica el almacenamiento de un número de empleado como NUMBER, un nombre y un apellido como VARCHAR2, y así sucesivamente. Mediante la creación de un Record para almacenar detalles de empleados, se crea una unidad lógica colectiva. Esto hace más fácil el acceso y la manipulación de los datos.

• PL/SQL Collections: Las Colecciones se utilizan para tratar datos como una sola unidad. Las colecciones son de tres tipos:
--Associative array.
--Nested table.
--VARRAY.

¿Por qué usar de datos compuestos?
Usted tiene todos los datos relacionados como una sola unidad. Puede acceder y modificar fácilmente los datos.  Si están compuesto, los datos resultan más fáciles de manejar, relacionar, y el transportar. Una analogía es tener solo una bolsa para todos los componentes de su computadora portátil en lugar de una bolsa separada para cada componente.

Si los Records y Colecciones  de PL/SQL son ambos tipos de datos compuestos, ¿Cómo elegir cuál usar?
• Utilice Records cuando se desea almacenar valores de diferentes tipos de datos que están relacionados lógicamente. Por ejemplo, puede crear un Record para contener detalles de empleados y indicar que todos los valores almacenados están relacionados, ya que proporcionan información sobre un empleado en particular.

• Utilice Colecciones cuando se desea almacenar valores del mismo tipo de dato. Tenga en cuenta que este tipo de dato también puede ser de tipo compuesto (por ejemplo, Records). Se puede definir una colección para almacenar el primer nombre de todos los empleados. Puede que haya guardado n nombres en la colección; Sin embargo, el nombre 1 no está relacionado con el nombre 2. La única relación posible es que son nombres de los empleados. Estas colecciones son similares a las matrices en lenguajes de programación como C, C++ y Java.
____________________________________________________________________________________
Records de PL/SQL.

Los Records de PL/SQL:
• Deben contener uno o más componentes (llamados campos) de cualquier tipo: escalarRecord o INDEX BY table.
• Son similares a las estructuras de la mayoría de lenguajes de tercera generación (incluyendo C y C ++).
• Son definidos por el usuario y pueden ser un subconjunto de una fila de una tabla.
• Trata una colección de campos como una unidad lógica.
• Son convenientes para extraer una fila de datos de una tabla para su procesamiento.

Un Record es un grupo de elementos de datos relacionados almacenados en campos, cada campo con su propio nombre y tipo de dato:
• Cada Record puede tener tantos campos como sea necesario.
• A los Records se les puede asignar valores iniciales y pueden definirse como NOT NULL.
• A los campos sin valores iniciales son inicializados con NULL.
• La palabra clave DEFAULT, así como := se pueden utilizar en la inicialización de campos.
• Se pueden definir tipos(types) de Records y declarar Records definidos por el usuario en la parte declarativa de cualquier bloque, subprograma o paquete.
• Se puede declarar y hacer referencia a los Records anidados. Un Record puede ser un componente de otro Record.

Sintaxis:
TYPE type_name IS RECORD
             (
                field_declaration[, 
                field_declaration]…
             );
--
field_declaration:
field_name {field_type | variable%TYPE 
           | table.column%TYPE | table%ROWTYPE}
           [[NOT NULL] {:= | DEFAULT} expr]
--
identifier  type_name;

En la sintaxis:
type_name
Es el nombre del tipo Record (Este identificador se utiliza para declarar las variables tipo Records).
field_name
Es el nombre de un campo dentro del Record.
field_type
Es el tipo de dato del campo (Representa cualquier tipo de dato PL/SQL excepto REF CURSOR. Puede utilizar los atributos %ROWTYPE y %TYPE).
expr
Es el valor inicial.
La restricción NOT NULL impide la asignación de valores nulos en los campos especificados. Asegúrese de inicializar los campos NOT NULL.

Ejemplo:
DECLARE
    TYPE client_typ_rec IS RECORD
        (
            codigo          NUMBER(6),
            nombre          VARCHAR2(35),
            direccion       VARCHAR2(50),
            telefono        VARCHAR2(10),
            fec_ingreso     DATE
        );
--
    v_cliente   client_typ_rec;
BEGIN
    v_cliente.codigo := 1;
    v_cliente.nombre := 'Julio Aponte';
    v_cliente.fec_ingreso := SYSDATE;
END;
/*En el ejemplo se crea un tipo recordclient_typ_rec, luego se crea una variable(v_cliente) de ese tipo record y posteriormente se le asigna valores a los campos de la variable en la sección ejecutable del bloque.*/
____________________________________________________________________________________
Atributo %ROWTYPE.
Ya vimos que %TYPE se utiliza para declarar una variable de tipo columna. Dicha variable tiene el mismo tipo de dato y tamaño que la columna de la tabla. El beneficio de %TYPE es que usted no tiene que cambiar la variable si se altera la columna. Además, si la variable es un número y se utiliza en los cálculos, usted no necesita preocuparse acerca de su precisión.

El atributo %ROWTYPE se utiliza para declarar un Record que contenga toda la fila de una tabla o vista. Los campos en el Record toman los nombres y tipos de datos de las columnas de la tabla o vista. El Record también puede almacenar toda una fila de datos obtenida de un cursor o variable de cursor.

Sintaxis:
DECLARE
   identifier     reference%ROWTYPE;

En la Sintaxis:
identifier
Es el nombre de la variable tipo record.
reference
Es el nombre de la tabla, vista, cursor, o variable de cursor en el cual se basa la variable record (Para que la referencia sea validad, la tabla o vista debe existir).
Ejemplo:
DECLARE
    v_rec_empleado      employees%ROWTYPE;
....
El Record v_rec_empleado tiene una estructura que consta de los siguientes campos, cada uno representando una columna en la tabla empleados.
(
   employee_id       NUMBER(6),
   first_name        VARCHAR2(20),
   last_name         VARCHAR2(20),
   email             VARCHAR2(20),
   phone_number      VARCHAR2(20),
   hire_date         DATE,
   salary            NUMBER(8,2),
   commission_pct    NUMBER(2,2),
   manager_id        NUMBER(6),
   department_id     NUMBER(4)
)
Asignando valores a los Records.
Puede asignar una lista de valores comunes a un registro utilizando la sentencias SELECT o FETCH. Asegúrese de que los nombres de columna aparezcan en el mismo orden que los campos en su Record. También puede asignar un Record a otro, si ambos tienen los mismos tipos de datos. Un Record de tipo employees%ROWTYPE y un tipo Record definido por el usuario con campos análogos de la tabla employees tendrán los mismos tipos de datos. Por lo tanto, si un Record definido por el usuario contiene campos similares a los de un Record %ROWTYPE, sera posible asignar ese Record definido por el usuario a el Record %ROWTYPE.

Las declaraciones de campos utilizados en la definición de un Record son como las declaraciones de variables. Cada campo tiene un nombre único y un tipo de datos específico. No hay tipos de datos predefinidos para los Records de PL/SQL, como en el caso de las variables escalares. Por lo tanto, se requiere crear el tipo Record en primer lugar, y luego declarar un identificador usando ese tipo.

Ejemplo:
SET SERVEROUTPUT ON
DECLARE
    TYPE t_rec IS RECORD
      (
       v_sal              NUMBER(8),
       v_minsal           NUMBER(8) DEFAULT 1000,
       v_fecha_contrato   employees.hire_date%TYPE,
       v_rec1             employees%ROWTYPE
      );
---
    v_mirec t_rec;
BEGIN
    v_mirec.v_sal := v_mirec.v_minsal + 500;
    v_mirec.v_fecha_contrato := SYSDATE;
---
    SELECT * INTO v_mirec.v_rec1
    FROM employees
    WHERE employee_id = 100;
---
    DBMS_OUTPUT.PUT_LINE(v_mirec.v_rec1.last_name
                            ||' '||to_char(v_mirec.v_fecha_contrato)
                            ||' '|| to_char(v_mirec.v_sal));
END;
---
En el ejemplo, un Record de PL/SQL se crea utilizando los dos pasos necesarios:
1. Se define un tipo Record (t_rec).
2. Se declara un Record (v_mirec) del tipo t_rec.
Nota:
• El Record contiene cuatro campos: v_sal, v_minsalv_fecha_contrato, y v_rec1.
• v_rec1 se define mediante el atributo %ROWTYPE, que es similar al atributo %TYPE. Con %TYPE, un campo hereda el tipo de dato de una columna especificada. Con %ROWTYPE, el campo hereda los nombres y tipos de datos de todas las columnas de la tabla referenciada.
• Los campos de un Record de PL/SQL se pueden referenciar mediante la notación <Record>.<campo>, o con <Record>.<campo>.<columna> para los campos que se definen con el atributo %ROWTYPE.
• Puede añadir la restricción NOT NULL a cualquier declaración de campo para evitar la asignación de valores nulos a ese campo. Recuerde que los campos declarados como NOT NULL deben inicializarse.

Ventajas del Atributo %ROWTYPE.
• No es necesario conocer el número y tipos de datos de las columnas de Base de Datos y, de hecho, dichas columnas podrían cambiar en tiempo de ejecución sin afectar el Record.
• El atributo %ROWTYPE es útil cuando se desea recuperar una fila con:
--La instrucción SELECT *
--Sentencias INSERT y UPDATE a nivel de fila.

Utilice el atributo %ROWTYPE cuando no está seguro acerca de la estructura de la tabla de Base de Datos subyacente.

La principal ventaja de utilizar %ROWTYPE es que simplifica el mantenimiento. Usando %ROWTYPE asegura que los tipos de datos de las variables declaradas con este atributo cambian dinámicamente cuando se altera la tabla subyacente. Si una instrucción DDL cambia las columnas de una tabla, el programa PL/SQL se invalida. Cuando se vuelve a compilar el programa, se refleja automáticamente el nuevo formato de tabla.

El atributo %ROWTYPE es particularmente útil cuando se desea recuperar toda una fila de una tabla. En ausencia de este atributo, se verá forzado a declarar una variable para cada columna devuelta por la sentencia SELECT.

Ejemplos:
CREATE TABLE emps_retirados
   (
    codigo        NUMBER(4),
    nombre        VARCHAR2(10),
    empleo        VARCHAR2(9),
    manager       NUMBER(4),
    fec_entrada   DATE
    fec_salida    DATE,
    salario       NUMBER(7,2), 
    comision      NUMBER(7,2),
    departameto   NUMBER(2)
  );
/*Creamos esta tabla que nos servirá de ejemplo.*/
---
DECLARE
    v_codigo_emp    NUMBER := 124;
    v_emp_rec       emps_retirados%ROWTYPE;
BEGIN
    SELECT
            employee_id,
            last_name,
            job_id,
            manager_id,
            hire_date,
            SYSDATE,
            salary,
            commission_pct,
            department_id
    INTO v_emp_rec
    FROM employees
    WHERE employee_id = v_codigo_emp;
---
    INSERT INTO emps_retirados
    VALUES v_emp_rec;
---
    COMMIT;
END;
/
SELECT *
FROM emps_retirados;
/*Este SCRIPT recupera los datos del empleado 124 de la tabla employees y luego los inserta en la tabla emps_retirados; notar como se simplifica la sentencia INSERT con el uso del atributo %ROWTYPE.*/
---OUTPUT:
DECLARE
    v_codigo_emp    NUMBER := 124;
    v_emp_rec       emps_retirados%ROWTYPE
BEGIN
    SELECT * INTO v_emp_rec
    FROM emps_retirados
    WHERE codigo = v_codigo_emp;
---
    v_emp_rec.nombre := 'Samira';
    v_emp_rec.empleo := 'Secre';
    v_emp_rec.fec_salida := SYSDATE-2;
---
    UPDATE emps_retirados
    SET ROW = v_emp_rec
    WHERE codigo = v_codigo_emp;
---
    COMMIT;
END;
/
SELECT *
FROM emps_retirados;
/*En este se recuperan los datos del mismo empleado 124 pero ahora de la tabla emps_retirados, luego se les hace algunas modificaciones a los datos y luego se actualiza el registro en la misma tabla; notar que simple luce la sentencia UPDATE con el uso del atributo %ROWTYPE.*/
____________________________________________________________________________________
Colecciones de PL/SQL.
Como se dijo anteriormente, se usan colecciones de PL/SQL cuando se desea almacenar valores del mismo tipo de datos. Este tipo de datos puede ser también de tipo compuesto (por ejemplo, Records).
Por lo tanto, las colecciones se usan para tratar los datos como una sola unidad. Las colecciones son de tres tipos:
• Associative Array O Matriz Asociativa.
• Nested Table.
• VARRAY.

Associative Array(INDEX BY tables).
Un Associative Array es un tipo de colección de PL/SQL, es un tipo de datos compuesto, y es definido por el usuario. Este tipo esta compuesto por conjuntos de pares key-value, entiéndase key como la llave primaria y value como el valor de la fila. Pueden almacenar datos utilizando una llave primaria(key) como el índice, donde la llave primaria no necesariamente tiene que ser secuencial. Los Associative Arrays son también conocidos como INDEX BY tables.

Los Associative Array tienen sólo dos columnas, a las cuales no es posible asignarles nombres:
• La primera columna, de tipo entero o carácter, actúa como la llave primaria.
• La segunda columna, de tipo escalar o Record, contiene los valores.

Ejemplo:
Estructura de los Associative Arrays.
Como se mencionó anteriormente, los Associative Arrays tienen dos columnas. La segunda columna puede contener un valor por cada fila, o varios valores.

Llave primaria única: El tipo de dato de esta columna puede ser:
• Numérico, ya sea BINARY_INTEGER o PLS_INTEGER. Estos dos tipos de datos numéricos requieren menos almacenamiento que los NUMBER, y las operaciones aritméticas sobre ellos son más rápidas que con NUMBER.
• VARCHAR2 o uno de sus subtipos.

Columna "Value": La columna Value puede ser de tipo de dato escalar o tipo de dato Record. Una columna con tipo de dato escalar sólo puede tener un valor por fila, mientras que una columna con tipo de dato Record puede contener varios valores por fila.

Otras características.
• Un Associative Array no se rellena en el momento de la declaración. No contiene llaves primarias o valores, y no se puede inicializar al momento de su declaración.
• Se requiere una sentencias ejecutable explícita para rellenar el Associative Array.
• Al igual que el tamaño de una tabla de Base de Datos, el tamaño de un Associative Array no tiene restricciones. Es decir, el número de filas puede aumentar de forma dinámica para que su Associative Array crezca a medida que se añaden nuevas filas. Tenga en cuenta que las llaves primarias no tienen que ser secuenciales, y pueden ser tanto positiva como negativa.

Sintaxis:
TYPE type_name IS TABLE OF 
    { column_type [NOT NULL] | variable%TYPE [NOT NULL]
    | table.column%TYPE [NOT NULL
    | table%ROWTYPE }
    INDEX BY { PLS_INTEGER | BINARY_INTEGER
    | VARCHAR2(<size>) } ;

identifier     type_name;

Ejemplo:
SET SERVEROUTPUT ON
DECLARE
    TYPE nombre_tab_typ
        IS TABLE OF employees.last_name%TYPE
        INDEX BY PLS_INTEGER;
    TYPE fec_contrato_tab_typ
        IS TABLE OF DATE
        INDEX BY PLS_INTEGER;
    t_nombre            nombre_tab_typ;
    t_fec_contrato     fec_contrato_tab_typ;
BEGIN
    t_nombre(1)   := 'Pepe';
    t_fec_contrato(1) :=  SYSDATE;
--
    SELECT  last_name,
            hire_date
    INTO    t_nombre(6),
            t_fec_contrato(6)
    FROM  employees
    WHERE employee_id = 111;
--
    FOR i IN t_nombre.FIRST..t_nombre.LAST LOOP
      IF t_nombre.EXISTS(i) THEN
        DBMS_OUTPUT.PUT_LINE(i||'. Nombre: '||t_nombre(i)||', Fecha: '||t_fec_contrato(i));
      ELSE
        DBMS_OUTPUT.PUT_LINE('Indice: '||i||' no asignado');
      END IF;
    END LOOP;
END;
/*En este ejemplo creamos un tipo tabla de nombres: nombre_tab_typ y un tipo tabla de fechas: fec_contrato_tab_typ; luego creamos una variable de cada tipo: t_nombre t_fec_contrato; en la sección ejecutable notamos como se asigna valores a dichas variables usando sus indices, lo cual se puede puede hacer manualmente o con una sentencia SELECT; también usamos un FOR LOOP para así recorrer nuestras tablas y mostrar los  valores de los indices no nulos. Nota: el método/función EXISTS es explicado mas adelante.*/
---OUTPUT:
Métodos INDEX BY table.
Un método INDEX BY table es una función incorporada(built-in function) que opera sobre un Associative Array y es invocada usando la notación de punto.

Sintaxis:
table_name.method_name[ (parameters) ]

Método

Descripción
EXISTS(n)
Devuelve TRUE si el índice n en el Associative Array existe.
COUNT
Retorna el numero de elementos contenidos en el  Associative Array .
FIRST
·         Retorna el primer   (mas pequeño) numero de indice del Associative Array.
·         Devuelve NULL si el Associative Array esta vació.
LAST
·         Retorna el último (mas grande) numero de indice del Associative Array.
·         Devuelve NULL si el Associative Array esta vació.
PRIOR(n)
Retorna el numero de indice que antecede al indice n en el Associative Array.
NEXT(n)
Retorna el numero de indice que sigue(sucede) al indice en el Associative Array.
DELETE
·         DELETE remueve todos los elemento del Associative Array.
·         DELETE(n) remueve el indice n en el Associative Array.
·         DELETE(m, n) remueve todos los elementos en el rango m, n del Associative Array.
Ejemplos:
SET SERVEROUTPUT ON
DECLARE
    TYPE dept_tab_typ
      IS TABLE OF departments%ROWTYPE
      INDEX BY PLS_INTEGER;
--
    tabla_dept dept_tab_typ; -- Cada elemento en tabla_dept es un Record.
BEGIN
    SELECT * INTO tabla_dept(1)
    FROM departments
    WHERE department_id = 10;
--
    DBMS_OUTPUT.PUT_LINE('Código: '||tabla_dept(1).department_id ||
                         ', Departamento de '||tabla_dept(1).department_name ||
                         ', Código supervisor: '||tabla_dept(1).manager_id);
END;
/*En el ejemplo creamos un tipo table de Recordsdept_tab_typ y creamos una variable(tabla_dept) de dicho tipo; luego asignamos el registro completo del departamento 10 de la tabla departments al indice 1 de nuestra variable; Posteriormente mostramos algunos de sus campos.*/
---OUTPUT:
SET SERVEROUTPUT ON
DECLARE
    TYPE dept_tab_typ
      IS TABLE OF departments%ROWTYPE
      INDEX BY PLS_INTEGER;
--
    tabla_dept dept_tab_typ; -- Cada elemento en tabla_dept es un Record.
    v_cod_dept  NUMBER(3);
BEGIN
    FOR i IN 1..10 LOOP
        v_cod_dept := i*10;
--
        SELECT * INTO tabla_dept(i)
        FROM departments
        WHERE department_id = v_cod_dept;
    END LOOP;
--
    FOR i IN tabla_dept.FIRST..tabla_dept.LAST LOOP
        DBMS_OUTPUT.PUT_LINE('Código: '||tabla_dept(i).department_id ||
                             ', Departamento de '||tabla_dept(i).department_name ||
                             ', Código supervisor: '||tabla_dept(i).manager_id);
    END LOOP;
--
    DBMS_OUTPUT.PUT_LINE('Cantidad Total: '||tabla_dept.COUNT);
END;
/*Aquí modificamos el ejemplo pasado; le agregamos dos FOR LOOP para así almacenar mas registros en nuestro associative array; Notar el uso de los métodos FIRST, LAST y COUNT..*/
---OUTPUT:
____________________________________________________________________________________
Los NESTED Table.
La funcionalidad de los NESTED Table (Tabla Anidada) es similar a la de los Associative Arrays; sin embargo, hay diferencias en su aplicación.
• Los NESTED Table son un tipo de dato válido a nivel de esquema, pero los Associative Arrays no. Por lo tanto, a diferencia de los Associative Arrays, los NESTED Table se pueden almacenar en la Base de Batos.
• El tamaño de un NESTED Table aumentar de forma dinámica, aunque el tamaño máximo es de 2 GB.
• La "Llave primaria" no puede ser un valor negativo (contrario a los Associative Arrays). Aunque se hace referencia a la primera columna como Llave primaria, no hay tal cosa en un NESTED Table. Mas bien es una columna con números.
• Los elementos pueden ser borrados desde cualquier parte, dejando espacios libres con una Llave primaria no secuencial. Las filas de un NESTED Table no están en ningún orden en particular.
• Al recuperar los valores de una tabla anidada, a las filas se le da subíndices consecutivos a partir de 1.

Nota: Para poder asignar valor a una variable tipo NESTED Table debe hacerlo invocando el nombre del tipo.

Sintaxis:
TYPE type_name IS TABLE OF
 {column_type | variable%TYPE
 | table.column%TYPE} [NOT NULL]
 | table.%ROWTYPE

Ejemplo:
SET SERVEROUTPUT ON
DECLARE
    TYPE typ_lugar
        IS TABLE OF locations.city%TYPE;
--
    v_oficinas typ_lugar;
BEGIN
   v_oficinas := typ_lugar
                        (
                          'San Juan',
                          'Tokyo',
                          'Singapore',
                          'Oakland',
                          'Quito'
                        );
    FOR i IN 1.. v_oficinas.COUNT LOOP
        DBMS_OUTPUT.PUT_LINE(v_oficinas(i));
    END LOOP;
END;
/**/
---OUTPUT:
____________________________________________________________________________________
Los VARRAY.
Una matriz de tamaño variable (VARRAY) es similar a un Associative Array, excepto que un VARRAY es limitado en tamaño.
• Un VARRAY es una tabla válida a nivel de esquema.
• Los elementos de tipo VARRAY se llaman VARRAYs.
• Los VARRAYs tienen un límite superior fijo. Se tiene que especificar el límite superior cuando se declaran. Esto es similar a las matrices en lenguaje C. El tamaño máximo de un VARRAY es de 2 GB, como en las tablas anidadas(NESTED Table).
• La distinción entre un NESTED Table y un VARRAY es el modo de almacenamiento físico. Los elementos de un VARRAY se almacenan en línea con los datos de la tabla a menos que el tamaño de la VARRAY sea mayor que 4 KB. Esto contrasta con los NESTED Table, que siempre se almacenan fuera de línea.
• Se puede crear un tipo VARRAY en la Base de Datos utilizando SQL.

Ejemplo:
SET SERVEROUTPUT ON
DECLARE
    TYPE varray_typ 
      IS VARRAY(65) OF VARCHAR2(35);
---
    varray_nombre     varray_typ;
    v_nombre          VARCHAR2(35);
---
    v_cont        NUMBER := 0;
BEGIN
    varray_nombre := varray_typ();
    FOR i IN 111..120 LOOP
---
        SELECT first_name||' '||last_name INTO v_nombre
        FROM employees
        WHERE employee_id = i;
---
        v_cont := v_cont+1;
        varray_nombre.EXTEND;
        varray_nombre(v_cont) := v_nombre;
---
        DBMS_OUTPUT.PUT_LINE('Nombre: '||varray_nombre(v_cont));
    END LOOP;
END;
/*Aquí mostramos el uso de los tipos VARRAYs; Notar que es necesario invocar el constructor(equivalente al nombre del tipo base) para así poder asignar valores a una variable tipo VARRAY y también es necesario invocar el método EXTEND cada vez que se desee agregar un nuevo valor.*/
---OUTPUT:
____________________________________________________________________________________
Resumen de los Tipos Colecciones.
Las Matrices Asociativas(Associative Arrays).
Son conjuntos de pares clave-valor, donde cada clave es única y se utiliza para localizar un valor correspondiente en la matriz. La clave puede ser Numérica o Tipo Carácter. El valor de la matriz puede ser de tipo de dato escalar (valor individual) o de tipo de dato de Record (múltiples valores).

Debido a que las matrices asociativas están destinados para almacenar datos temporales, no se puede utilizar con sentencias SQL como INSERT.

Las Tablas Anidadas(NESTED Table).
Una tabla anidada tiene un conjunto de valores. En otras palabras, es una tabla dentro de una tabla. Las tablas anidadas son ilimitadas; es decir, el tamaño de la tabla puede aumentar de forma dinámica. Las tablas anidadas están disponibles tanto en PL/SQL como en la Base de Datos. Dentro de PL/SQL, las tablas anidadas son como matrices unidimensionales, cuyo tamaño puede aumentar de forma dinámica.

Las Matrices de Tamaño Variable(Varrays).
Son colecciones de elementos homogéneos que tienen un número fijo de elementos (aunque se puede cambiar el número de elementos en tiempo de ejecución). Ellos usan números secuenciales como subíndices. Es posible crear estos tipos en SQL, por lo cual se pueden almacenar en tabla de la Base de Datos.
_____________________________________________________________________________________
Fuente: Oracle Database: PL/SQL Fundamentals