martes, 18 de julio de 2017

Mensajes y Alertas de Oracle Forms

Objetivos:


 Describir el comportamiento predeterminado de los mensajes de Oracle Forms.
 Controlar errores de subprogramas built-ins en tiempo de ejecución.
 Identificar los tipos de mensajes de Oracle Forms.
 Controlar mensajes del sistema.
 Crear y controlar alertas.
 Manejar errores del servidor de base de datos.

NOTA: Usamos como ejemplo la Base de Datos: ORCL (Esquema: OE), la cual viene por defecto en cualquier versión de ORACLE.
______________________________________________________________________________________
Mensajes y Alertas en Tiempo de Ejecución.
Forms muestra mensajes en tiempo de ejecución para informar al usuario sobre eventos que ocurren en la sesión. Como diseñador, dependiendo de la naturaleza de la aplicación, es posible que desee suprimir o modificar algunos de estos mensajes.
Forms puede comunicarse con el usuario de las siguientes maneras:
 Mensaje Informativo: Mensaje que indica al usuario el estado actual del proceso o proporciona información sensible del contexto. Por defecto, esto mensajes se muestran en la línea de mensajes. Puede suprimir y/o modificar su apariencia con el Trigger On-Message.
 Mensaje de Error: Informa al usuario de un error que impide la acción actual. Por defecto, esto mensajes se muestran en la línea de mensajes. Puede suprimir y/o modificar errores con el Trigger On-Error.
 Mensaje de Trabajo (Working Message): Indica al usuario que el formulario está procesando (Ejemplo: Trabajando...). Esto se muestra en la línea de mensaje. Este tipo de mensaje puede suprimirse estableciendo la variable de sistema SUPPRESS_WORKING en TRUE:
:SYSTEM.SUPPRESS_WORKING := 'TRUE';
 Alerta del Sistema: Estas alertas proporcionan información al usuario y requieren que el mismo proporcione algún consentimiento o respuesta a una pregunta antes de que el procesamiento pueda continuar. Se muestra como una ventana modal.

Nota: Cuando existe más de un mensaje en cola (esperando para ser mostrado en la línea de mensajes), el mensaje actual se muestra como una alerta.

También puede crear mensajes y alertas en su aplicación:
 Mensaje de Aplicación: Estos son mensajes que crea en su aplicación mediante el built-in MESSAGE. Por defecto se muestran en la línea de mensajes.
 Alerta de Aplicación: Son las alertas que diseña como parte de su aplicación las cuales esperan una respuesta por parte del usuario. Estas alertas son mostradas utilizando la función SHOW_ALERT (built-in).
______________________________________________________________________________________
Manejando Errores en Tiempo de Ejecución.
Cuando un subprograma built-in falla, este no causa una excepción en el Trigger o Program Unit del cual es llamado. Esto significa que la ejecución continúa con el código subsiguiente después del error, a menos que tome medidas para detectar dicho error.

Ejemplo:
Un botón (Stock_Button) en el bloque CONTROL se encuentra en el Toolbar Canvas del form ORDERS. Al hacer clic en él, el Trigger When-Button-Pressed navega al bloque INVENTORIES y realiza una consulta allí.
GO_BLOCK ('INVENTORIES');
EXECUTE_QUERY;
Si el built-in GO_BLOCK falla porque el bloque INVENTORIES no existe o porque no es accesible, el procedimiento EXECUTE_QUERY intentaría realizar una consulta en dicho bloque.

Funciones built-in para detectar el éxito o fracaso.
Forms Builder proporciona funciones que indican si la acción más reciente en el Form tuvo éxito.
Función Built-In
Valor de Retorno
FORM_SUCCESS
TRUE: Acción exitosa.
FALSE: Ocurrió algún Error (puede ser simple o fatal).
FORM_FAILURE
TRUEOcurrió un Error (No fatal).
FALSESi ocurrió un error fue fatal.
FORM_FATAL
TRUEOcurrió un Error fatal.
FALSE: Si ocurrió un error no fue fatal.
Nota: Estas funciones built-in devuelven el estado de éxito o fracaso de la acción más reciente en el Form. La acción fallida puede haber ocurrido en un Trigger que disparó como resultado de un built-in en un otro Trigger. Por ejemplo, el procedimiento EXECUTE_QUERY, puede provocar que se desencadene el Trigger Pre-Query, y este último fallar.
______________________________________________________________________________________
Errores y Built-ins.
Por lo general resulta más práctico utilizar FORM_SUCCESS, esto debido a que devuelve FALSE si se produce algún error (fatal o no fatal). Luego, puede codificar el Trigger para que tome la acción apropiada.

Ejemplo de FORM_SUCCESS:
En el siguiente ejemplo, la función FORM_SUCCESS se utiliza en una condición para decidir si la consulta debe realizarse, dependiendo del éxito de la acción GO_BLOCK.
GO_BLOCK('INVENTORIES');
IF FORM_SUCCESS THEN
EXECUTE_QUERY;
ELSE
MESSAGE('An error occurred while navigating to Stock');
END IF;

Los Triggers fallan sólo si hay una excepción no controlada o si usted levanta la excepción FORM_TRIGGER_FAILURE el cual hace que el Trigger falle de una manera controlada.

Nota: La unidad de programa CHECK_PACKAGE_FAILURE, que se genera al construir bloques de detalle maestro, puede ser llamada para hacer fallar un Trigger si la última acción no se ha realizado correctamente.

Funciones Built-ins para determinar el error.
Cuando detecta un error, es posible que deba identificarlo para realizar una acción específica. Tres funciones built-ins proporcionan esta información:
Función
Descripción del Valor Retornado
ERROR_CODE
Número de error (datatype NUMBER).
ERROR_TEXT
Descripción del error (datatype CHAR).
ERROR_TYPE
FRM= Indica un error de Forms Builder, ORA= Indica un error de Oracle (datatype CHAR).
______________________________________________________________________________________
Niveles de Severidad de los Mensajes.
Supresión de mensajes según su Severidad.
Puede evitar la emisión de mensajes del sistema, en función de su nivel de severidad. Forms Builder clasifica cada mensaje con un nivel de severidad que indica qué tan crítica o trivial es la información; Cuanto más altos sean los números, más crítico será el mensaje. Hay seis niveles que puedes afectar.
Nivel de Severidad
Descripción
0
Todos los mensajes.
5
Reafirma una condición obvia.
10
El usuario ha cometido un error de procedimiento.
15
El usuario intenta una acción para la cual el Form no está diseñado
20
No se puede continuar la acción deseada debido a un problema en el Trigger o a alguna otra condición excepcional.
25
Una condición que podría provocar que el Form se ejecute incorrectamente.
> 25
Mensajes que no pueden ser suprimidos.
En un Trigger, puede especificar que sólo los mensajes por encima de un nivel de severidad especificado deben ser emitidos por el Form. Esto se hace asignando un valor a la variable de sistema MESSAGE_LEVEL. Forms entonces sólo emite mensajes que están por encima del nivel de severidad definido en esta variable.
El valor predeterminado para MESSAGE_LEVEL (al inicio del Form) es 0. Esto significa que se muestran los mensajes de todas las severidades.

______________________________________________________________________________________
Supresión de Mensajes.
Como habíamos mencionado, puede suprimir la aparición de ciertos mensajes de acuerdo a su nivel de severidad. A continuación un ejemplo de como hacerlo:
:SYSTEM.MESSAGE_LEVEL := '5';
UP;
IF  NOT FORM_SUCCESS THEN
  MESSAGE('Already at the first Order');  
END IF;    
:SYSTEM.MESSAGE_LEVEL := '0';


El anterior Trigger usa el Built-in UP para mover o cambiar el cursor del registro actual al que se encuentra arriba. Si el cursor ya está en el primer registro, el built-in falla y aparece el siguiente mensaje: FRM-40100: At first record.
FRM-40100 se trata de un mensaje de nivel de severidad 5. Sin embargo, el Trigger lo suprime y emite su propio mensaje de aplicación, mas adelante se restablece el nivel de mensaje a normal (0).

Supresión del Mensaje Trabajando (Working).
El mensaje Working se muestran cuando Forms está ocupado procesando una acción. Por ejemplo, al realizar una consulta. Puede suprimir este mensaje estableciendo la variable de sistema SUPPRESS_WORKING en True:
:SYSTEM.SUPPRESS_WORKING := 'TRUE';
Nota: Puede configurar estas variables de sistema tan pronto como se inicie el Form, si es necesario, realizando las asignaciones en el Trigger WHEN-NEW-FORM-INSTANCE.
______________________________________________________________________________________
La Excepción FORM_TRIGGER_FAILURE.
Los Triggers fallan cuando ocurre uno de los siguientes:
 Se levanta una excepción no controlada.
 Cuando solicita la interrupción del Trigger al generar la excepción Built-in FORM_TRIGGER_FAILURE.

Esta excepción está definida y gestionada por Forms Builder. Puede levantar esta excepción:
 En la parte ejecutable de un Trigger, para saltarse las acciones restantes y hacer fallar el Trigger.
 En un manejador de excepciones, para detener el Trigger después de que se hayan cumplido sus propias acciones de gestión de excepciones.
En cualquier caso, Forms Builder tiene su propio manejador de excepciones para FORM_TRIGGER_FAILURE, que hace fallar el Trigger pero no causa una excepción no controlada. Esto significa que puede hacer fallar el Trigger de manera controlada.

Ejemplo:
SELECT cust_first_name ||' '|| cust_last_name
  INTO :ORDERS.customer_name
  FROM CUSTOMERS
  WHERE customer_id = :ORDERS.customer_id;  
EXCEPTION
WHEN no_data_found THEN 
  MESSAGE('Customer with this ID not found');
  RAISE form_trigger_failure;

Este ejemplo agrega una acción al manejador de excepciones del Trigger When-Validate-Item para el elemento Customer_ID. Si el usuario introduce un número de cliente no existente, se muestra el mensaje: Customer with this ID not found y el Trigger falla.
______________________________________________________________________________________
Triggers para interceptar Mensajes del Sistema.
Al escribir Trigger que activan eventos de mensajes, puede interceptar los mensajes del sistema antes de que se muestren en la pantalla. Para ello usamos los siguientes Triggers:
 On-Error: Disparan cuando ocurre un mensaje de error del sistema.
 On-Message: Disparan cuando ocurre un mensaje informativo del sistema.

Estos Triggers reemplazan la visualización de un mensaje, de modo que el operador no ve ningún mensaje a menos que emita uno del propio Trigger.

Puede definir estos Triggers en cualquier nivel. Por ejemplo, un Trigger On-Error en el nivel de Item sólo intercepta los mensajes de error que se producen mientras el control está en ese Item. Sin embargo, si define uno o ambos Triggers en el nivel de Form, podrá reemplazar todos los mensajes sin importar el objeto que los provoque.

Trigger On-Error.
Utilice este Trigger para:
 Detectar errores Oracle y errores de Forms. Este Trigger puede realizar acciones correctivas basadas en el error que ocurrió.
 Reemplazar el mensaje de error predeterminado con un mensaje personalizado para esta aplicación.

Recuerde que puede utilizar las funciones built-in: ERROR_CODE, ERROR_TEXT y ERROR_TYPE para identificar los detalles del error y, posiblemente, utilizar esta información en su propio mensaje.

Ejemplo:

IF ERROR_CODE = 40202 THEN

  MESSAGE('You must fill in this field for an Order');

ELSE

  MESSAGE(ERROR_TYPE || '-' || TO_CHAR(ERROR_CODE) ||   ': ' || ERROR_TEXT);

END IF;

RAISE FORM_TRIGGER_FAILURE;

Este Trigger On-Error envía un mensaje personalizado para el error 40202 (You must fill in this field for an Order), y reconstruye el mensaje estándar del sistema para todos los demás errores.

Trigger On-Message.
Utilice este Trigger para suprimir mensajes informativos, reemplazándolos con mensajes personalizados, según corresponda.

Puede manejar mensajes en On-Message de manera similar a On-Error. Sin embargo, debido a que este Trigger se activa debido a mensajes informativos, debe utilizar diferentes Built-ins para determinar la naturaleza del mensaje actual.
Built-In
Descripción
MESSAGE_CODE
Número del mensaje (tipo de datos NUMBER).
MESSAGE_TEXT
Texto del mensaje informativo que se mostraría (tipo de datos CHAR).
MESSAGE_TYPE
FRM = Mensaje de Form Builder.
ORA = Mensaje del Servidor Oracle.
NULL = No se ha emitido ningún mensaje en esta sesión (tipo de datos CHAR).
Nota: Estas funciones devuelven información sobre el mensaje más reciente que se ha emitido. Si sus aplicaciones deben estar soportadas en más de un idioma nacional, utilice MESSAGE_CODE en preferencia a MESSAGE_TEXT al comprobar un mensaje.

Ejemplo:
      IF  MESSAGE_CODE in (40350,40301) THEN
  MESSAGE('No Orders found-check your search values');
ELSE
  MESSAGE(MESSAGE_TYPE || '-' || TO_CHAR(MESSAGE_CODE)   ||': '|| MESSAGE_TEXT);
END IF;
Este Trigger modifica el mensaje "Query caused no records to be retrieved" (40350) y “Query caused no records to be retrieved. Re-enter.” Mensaje (40301).
______________________________________________________________________________________
Las Alertas y sus Propiedades.
Las alertas son un método alternativo para comunicarse/interactuar con el usuario. Debido a que se muestran en una ventana modal, las alertas proporcionan una forma eficaz de llamar la atención y obligar al operador a contestar el mensaje antes de que el procesamiento pueda continuar.
Utilice las alertas cuando necesite realizar lo siguiente:
 Mostrar un mensaje que el usuario no puede ignorar, y debe reconocer.
 Preguntar al usuario una pregunta en la que son adecuadas hasta tres respuestas (normalmente , No o Cancelar).

Maneje las respuestas a una alerta mediante el uso de subprogramas Built-ins. Por lo tanto, las alertas se gestionan en dos etapas:
 Cree la alerta en tiempo de diseño y defina sus propiedades en la paleta Propiedad.
 Activar la alerta en tiempo de ejecución mediante built-ins y tomar medidas basadas en la respuesta devuelta del operador.

Crear una Alerta.
Al igual que otros objetos que se crean en el tiempo de diseño, se crean alertas desde el Navegador de objetos.
 Seleccione el nodo Alertas en el Navegador y, a continuación, seleccione Crear.
 Defina las propiedades de la alerta en la paleta de propiedades.

Las propiedades que puede especificar para una alerta son las siguientes:
Propiedad
Descripción
Name
Es el nombre de la alerta.
Title
Título a mostrar en la alerta.
Alert Style
Símbolo mostrado en la alerta: Stop, Caution, o Note
Button1, Button2, Button3 Labels
Etiquetas u opciones para cada uno de los 3 posibles botones (NULL indica que el botón no será mostrado).
Default Alert Button
Especifica el botón que se presionara cuando el usuario presiona [Enter].
Message
Mensaje mostrado en la alerta -  puede tener múltiples líneas, con un máximo de 200 caracteres.
Planificando Alertas: ¿Cuántas Necesita?
Potencialmente, puede crear una alerta para cada mensaje que necesite mostrar, pero esto suele ser innecesario. Puede definir o modificar el mensaje de una alerta en tiempo de ejecución, antes de que se muestre al usuario. Se puede utilizar una sola alerta para mostrar muchos mensajes, siempre que sus botones sean adecuados para responder a los mensajes.
Crear una alerta para cada combinación de:
 Estilo de Alerta.
 Conjunto de botones (y etiquetas) para la respuesta del usuario.

Por ejemplo, una aplicación puede requerir una alerta de tipo Note con un solo botón (Aceptar) para confirmación, una alerta tipo Caution con un botón similar y dos alertas Stop en las cuales cada una proporcione una combinación diferente de botones para una respuesta. A continuación, puede asignar un mensaje a la alerta adecuada antes de su visualización, mediante el procedimiento Built-in SET_ALERT_PROPERTY.

______________________________________________________________________________________
Controlando Alertas en Tiempo de Ejecución.
Hay subprogramas Built-ins que permiten cambiar el mensaje de una alerta y sus etiquetas.  También hay Built-ins para mostrar la alerta, los cuales devuelven la respuesta del usuario al Trigger de llamada.

Procedimiento SET_ALERT_PROPERTY.
Utilice este Built-in para cambiar el mensaje o título que está asignado actualmente a una alerta.
SET_ALERT_PROPERTY ('alert_name', property, 'mensaje/title');

Parámetro
Descripción
Alert_name
Nombre de la alerta definido en Forms Builder (De forma alternativa puede especificar el alert_id de la misma).
Property
Propiedad a establecer (Use ALERT_MESSAGE_TEXT para el mensaje y TITLE para el título).
Message/Title
Cadena de caracteres que define el mensaje/título.
Procedimiento SET_ALERT_BUTTON_PROPERTY.
Utilice este Built-in para cambiar la etiqueta en uno de los botones de la alerta:
SET_ALERT_BUTTON_PROPERTY('alert_name', button, property, 'value');
Parámetro
Descripción
Alert_name
Nombre de la alerta definido en Forms Builder (De forma alternativa puede especificar el alert_id de la misma).
Button
El número que especifica el botón de la alerta (Use las constantes ALERT_BUTTON1, ALERT_BUTTON2, y ALERT_BUTTON3.)
Property
Propiedad a establecer; utilice LABEL.
Value
Cadena de caracteres que define el Label o Etiqueta.
Función SHOW_ALERT.
SHOW_ALERT es utilizada para mostrar una alerta en tiempo de ejecución, dicha función devuelve la respuesta del usuario al trigger de llamada:
selected_button := SHOW_ALERT('alert_name');

alert_name es el nombre de la alerta, tal como se define en Forms Builter. También puede especificar un Alert_Id para este argumento.

SHOW_ALERT devuelve una constante NUMBER, que indica cuál de los tres posibles botones presionó el usuario. Estos números corresponden a los valores de tres constantes PL/SQL, que están predefinidas por Forms Builder.

Después de mostrar una alerta que tiene más de un botón, puede determinar qué botón pulsó el usuario comparando el valor devuelto con las constantes correspondientes.

Ejemplo:
   IF SHOW_ALERT('del_check') = ALERT_BUTTON1 THEN
  DELETE_RECORD;
END IF;

En el ejemplo un Trigger que se activa cuando el usuario intenta eliminar un registro, antes de eliminar el registro la alerta se invoca esperando la confirmación de usuario. Si el usuario selecciona , se llama a la función DELETE_RECORD para eliminar el registro actual del bloque.
______________________________________________________________________________________
Dirigir Errores a una Alerta.
Es posible que desee mostrar errores en una alerta de forma automática, a través de un Trigger "On-Error". Las funciones Built-ins que devuelven informaciones del error, como ERROR_TEXT, se pueden utilizar en el procedimiento SET_ALERT_PROPERTY para construir el mensaje de una alerta.

Ejemplo:
  PROCEDURE alert_on_failure IS
  n NUMBER;
BEGIN
  SET_ALERT_PROPERTY('
error_alert',
  ALERT_MESSAGE_TEXT,
  ERROR_TYPE||'-'||TO_CHAR(ERROR_CODE)||': ' ||
  ERROR_TEXT);
  n := SHOW_ALERT('
error_alert');
END;
El anterior procedimiento (alert_on_failure) se puede llamar cuando la última acción de un Form no tuvo éxito. El procedimiento falla en el Trigger de llamada y muestra Error_Alert que contiene la información de error.

Nota: Si desea que el Trigger falle, incluya una llamada a RAISE form_trigger_failure.
______________________________________________________________________________________
Manejo de Errores Generados por el Servidor Oracle.
Los errores del Servidor de Base de Datos Oracle pueden ocurrir por muchas razones diferentes, como violar una restricción declarativa o encontrar un error en una Unidad de Programa. Usted debe saber cómo manejar los errores que pueden ocurrir en diferentes situaciones.

Causas de los errores del servidor Oracle.

Causa

Mensaje de Error
Algún Constraint
Causa un error predefinido.
Trigger de Base de Datos
Mensaje de error especificado en RAISE_APPLICATION_ERROR.
Program Unit
Mensaje de error especificado RAISE_APPLICATION_ERROR.
Tipos de Sentencias DML.
Las infracciones de restricciones tipo Constraint y la ejecución de Triggers de base de datos se deben a sentencias DML. Para fines de manejo de errores, debe distinguir entre los siguientes dos tipos de sentencias DML:
Tipo
Descripción
DML Implícito
Son sentencias DML  que están asociadas con bloques de base de datos. DML Implícito es también conocido como base table DML.  Por defecto, Forms construye y emite estas sentencias DML.
DML Explícito
Son sentencias DML que el desarrollador codifica en Triggers y Unidades de Programas.
Mensajes de error FRM causados por errores DML implícitos.
Si una sentencia DML implícita provoca un error del servidor Oracle, Forms muestra uno de estos mensajes de error:
 FRM-40508: ORACLE error: unable to INSERT record.
 FRM-40509: ORACLE error: unable to UPDATE record.
 FRM-40510: ORACLE error: unable to DELETE record.

Puede utilizar ERROR_CODE para interceptar estos errores en un Trigger On-Error y, a continuación, utilizar DBMS_ERROR_CODE y DBMS_ERROR_TEXT para determinar el código de error ORA y el mensaje.

Mensajes de error con Web-Deployed Forms.
Los usuarios pueden recibir un error genérico FRM-99999. Puede obtener información significativa acerca de este error desde el panel de control de JInitiator.
______________________________________________________________________________________
Capturar diferentes tipos de errores del Servidor Oracle.
Tipo
Manejo de Errores
DML Implícito
Utilice los Built-ins DBMS_ERROR_CODE DBMS_ERROR_TEXT en un Trigger On-Error.
DML Explícito
Utilice las funciones de PL/SQL SQLCODE SQLERRM en un manejador de excepciones WHEN   OTHERS de un Trigger o Unidad de Programa que ejecute sentencias DML.
Program Unit
Utilice las funciones de PL/SQL SQLCODE SQLERRM en un manejador de excepciones WHEN   OTHERS de un Trigger o Unidad de Programa el cual llama al Program Unit en cuestión.
Nota: Las violaciones a Constraints y Triggers de base de datos pueden ser causados tanto por DML implícito como explícito. Las unidades de programa siempre se llaman explícitamente desde un Trigger o desde otra unidad de programa.

Nota Técnica:
Los valores de DBMS_ERROR_CODE y DBMS_ERROR_TEXT son iguales a lo que el usuario vería después de seleccionar Ayuda>Mostrar error; Los valores no se restablecen automáticamente después de una ejecución exitosa.
______________________________________________________________________________________
Fuente: Oracle Forms Developer 10g: Build Internet Applications.

domingo, 2 de julio de 2017

Procedimiento: Dynamic Table Query



Objetivos:
• Crear un procedimiento que consulte (dinámicamente) cualquier tabla en tu esquema.
• Familiarizarnos con el SQL Dinámico.
• Ver ejemplos de uso práctico.


NOTA: Usamos como ejemplo la Base de Datos: ORCL, la cual viene por defecto en cualquier versión de ORACLE.
____________________________________________________________________________________
Introducción.
La intención fue crear un procedimiento que reciba el nombre de una tabla mas un valor con el cual se filtrara un consulta. El procedimiento espera el nombre de una tabla que pertenezca al esquema actual; En él, se busca la columna PRIMARY KEY de la tabla recibida para luego usarla como columna de filtro, de no existir un PRIMARY KEY el filtro se realizaría por la primera columna de la tabla. Es prudente destacar que no importa la cantidad de columnas que contenga la tabla, la consulta debe realizarse sin problemas y luego mostrar los resultados por pantalla.

Nota: El procedimiento está pensado para tablas que tengan máximo un PRIMARY KEY (o ninguno), de tener mas de uno, el filtro puede ejecutarse por una columna no deseada.

Los detalles de las técnicas usadas están al final de la definición del procedimiento.
____________________________________________________________________________________
Soporte.
El procedimiento usa SQL Dinámico. Para mas detalles sobre el mismo, ver las siguientes publicaciones:

 SQL Dinámico y Sentencias DDL en PL/SQL

____________________________________________________________________________________
Procedimiento proc_dynamic_table_query.

CREATE OR REPLACE PROCEDURE   proc_dynamic_table_query
                                                       (
                                                        p_table     IN  VARCHAR2,
                                                        p_filter    IN  VARCHAR2
                                                       )    IS
    e_wrong_table   EXCEPTION;

    CURSOR  cur_check_cols IS
        SELECT
                tc.column_name
        FROM    user_tab_columns tc
        WHERE   tc.table_name  =   UPPER(p_table)
        ORDER BY    tc.column_id    ASC;

    CURSOR  cur_check_pk    IS
        SELECT
                cc.column_name
        FROM    user_cons_columns cc, user_constraints c
        WHERE   cc.table_name       =   UPPER(p_table)
        AND     c.constraint_type   =   'P'
        AND     cc.table_name       =   c.table_name
        AND     cc.constraint_name  =   c.constraint_name;

    v_select        VARCHAR2(2000)   :=  'SELECT ';
    v_query         VARCHAR2(2000);
    v_primary       VARCHAR2(30);
    v_dynamic_type  VARCHAR2(2000)    :=  'TYPE   typ_dynamic IS RECORD (';
    v_count_cols    NUMBER           :=  0;
    v_output        VARCHAR2(2000)   :=  'DBMS_OUTPUT.PUT_LINE(';
    v_execute       VARCHAR2(4000);

    TYPE    typ_tab_cols      IS
                    TABLE   OF  VARCHAR2(30)
                        INDEX   BY BINARY_INTEGER;
    v_tab_cols      typ_tab_cols;
BEGIN

    FOR rec_cols    IN  cur_check_cols  LOOP
        v_count_cols    :=  v_count_cols+1;

        v_tab_cols(v_count_cols)    :=  rec_cols.column_name;

        v_select        :=  v_select||rec_cols.column_name||',';
        v_dynamic_type  :=  v_dynamic_type||rec_cols.column_name||' '||p_table||'.'||
rec_cols.column_name||'%TYPE,';
        v_output        :=  v_output||'v_tab(i).'||rec_cols.column_name||q'{||','||}';

    END LOOP;

    IF  LENGTH(v_select) =   7 THEN
        RAISE   e_wrong_table;
    END IF;

    v_select     :=  SUBSTR(v_select, 1 LENGTH(v_select)-1);
    v_dynamic_type     :=  SUBSTR(v_dynamic_type, LENGTH(v_dynamic_type)-1)||');';
    v_output            :=  SUBSTR(v_output, LENGTH(v_output)-7)||');';

    OPEN    cur_check_pk;
    FETCH   cur_check_pk  INTO  v_primary;
    CLOSE   cur_check_pk;

    DBMS_OUTPUT.PUT_LINE('Table: '||p_table||CHR(10)||'Columns:');
    DBMS_OUTPUT.PUT_LINE(SUBSTR(v_select, 8 LENGTH(v_select)));

    v_query     :=  CHR(10)||'FROM '||p_table||CHR(10)||'WHERE TO_CHAR('||
NVL(v_primary, v_tab_cols(1))||q'{) = :v_filter}';

    v_execute   :=      'DECLARE
                            '||v_dynamic_type||'
                            TYPE    typ_tab IS
                                TABLE   OF typ_dynamic
                                    INDEX BY BINARY_INTEGER;
                            v_tab   typ_tab;
                        BEGIN
                            '||v_select||' BULK COLLECT INTO v_tab'||v_query||';

                            FOR i IN NVL(v_tab.FIRST, 1)..NVL(v_tab.LAST,0) LOOP
                                '||v_output||'
                            END LOOP;
                        END;
                        ';

    EXECUTE IMMEDIATE   v_execute
                          USING   p_filter;

    DBMS_OUTPUT.PUT_LINE(CHR(10)||'Filter Applied: '||
 NVL(v_primary, v_tab_cols(1))||' = '||p_filter);

    EXCEPTION
        WHEN    e_wrong_table   THEN
            DBMS_OUTPUT.PUT_LINE('Invalid Table Name!');
END proc_dynamic_table_query;
/
/* En el anterior ejemplo:
• Se usó el CURSOR cur_check_cols para extraer todas la columnas de tabla recibida como parámetro.
• El CURSOR cur_check_pk busca la columna PRIMARY KEY por la cual se realizará el filtro.
• Las variables(v_selectv_queryv_primaryv_outputv_execute) son usadas para almacenar la sentencia SELECT usada en la sentencia dinámica.
• Usamos v_dynamic_type para almacenar la definición de un tipo Record usado en la sentencia dinámica.
• El CURSOR cur_check_cols es usado en forma de CURSOR FOR LOOP; Dentro de dicho LOOP asignamos a las variables sus valores correspondientes.
• El IF  LENGTH(v_select) =   7 tiene como fin validar que la variable v_select tenga una cadena mas extensa a la que se le asignó al momento de su declaración, si el CURSOR cur_check_cols  no retorna registros dicha variable tendría la longitud de 7 caracteres. Si dicho IF resulta TRUE la excepción e_wrong_table es levantada y las demás sentencias del procedimiento no se ejecutan.
• Si el anterior IF retorna FALSE en las siguientes sentencias usamos la función SUBSTR para eliminar algunos caracteres no necesarios de las variables ya mencionadas.
• Las demás sentencias se encargan de arma el bloque dinámico de forma adecuada para luego ejecutarlo con EXECUTE IMMEDIATE.
*/
____________________________________________________________________________________
Ejemplos de Implementación.
SET SERVEROUTPUT ON
BEGIN
    proc_dynamic_table_query('employees','100');
END
/
/*En este ejemplo usamos el procedimiento proc_dynamic_table_query pasandole el nombre de la tabla EMPLOYEES y el código de empleado 100, mas abajo podemos ver el OUTPUT.*/
---OUTPUT:
SET SERVEROUTPUT ON
BEGIN
    proc_dynamic_table_query('JOBS','AD_VP');
END
/
/*En este ejemplo vemos como el procedimiento proc_dynamic_table_query  funciona de la manera esperada con una tabla (JOBS) con menos campos y con un PRIMARY KEY tipo carácter.*/
---OUTPUT:
SET SERVEROUTPUT ON
BEGIN
    proc_dynamic_table_query('COUNTRIES','AR');
END
/
/*En este ejemplo usamos la tabla COUNTRIES.*/
---OUTPUT:
SET SERVEROUTPUT ON
BEGIN
    proc_dynamic_table_query('EMP_COPY','201');
END
/
/*En esta ocasión usamos como ejemplo la tabla EMP_COPY la cual es una copia de EMPLOYEES (Los mismos campos) pero sin PRIMARY KEY.*/
---OUTPUT:
SET SERVEROUTPUT ON
BEGIN
    proc_dynamic_table_query('TABLE1','2');
END
/
/*Ahora usamos la tabla TABLE1 la cual no tiene PRIMARY KEY y contiene varios registros en los cuales coinciden los valores de su primera columna. El fin de este ejemplo es mostrar como el procedimiento imprime varios registros si los mismos cumplen con el criterio de búsqueda.*/
---OUTPUT: