SQL (Structured Query Language)

Lenguaje de consulta estructurado relacional. Bases de datos. Aplicaciones. Procedural Language (PL): Controles

  • Enviado por: Samuel Suero
  • Idioma: castellano
  • País: España España
  • 20 páginas
publicidad
publicidad

Desarrollo de aplicaciones en lenguajes de 4º generación

2º Evaluación

El lenguaje SQL como lenguaje Huésped

El lenguaje SQL puede actuar como si fuera un lenguaje huésped, es decir, que puede interactuar dentro de un lenguaje distinto como puede ser el cobol, el visual Basic, el C++, etc.

Un ejemplo de esto en c sería:

#include <stdio.h>

EXEC SQL BEGIN DECLARE SECTION;

CHAR dni[6];

CHAR cod_cli[7];

CHAR user_name[7];

CHAR password[8];

FLOAT cuota;

EXEC SQL END DECLARE SECTION;

VOID sql_error ( );

MAIN ( )

{

EXEC SQL WHENEVER sqlerror DO sql_error (“Error Oracle”)

...

...

EXEC SQL CONNECT :username IDENTIFY BY :password;

EXEC SQL SELECT cod_cli INTO :cod_cli FROM clientes WHERE dni = :dni;

...

...

EXEC SQL COMMIT RELEASE;

EXIT(0)

}

VOID sql_error (CHAR *msg)

{

PRINTF (“\N%S”,msg)

EXEC SQL ROLLBACK RELEASED

EXIT(1);

}

El oracle mantiene una variable llamada SQLSTATE en la cual guarda codigos de retorno cuando se interactua con el. De esta forma se puede saber si se ha producido un error a la hora de manejar el oracle. Otras variables de este tipo son SQLCODE y SQLERR.

Para hacer el control de errores se ha escrito la linea:

EXEC SQL WHENEVER sqlerror DO sql_error (“Error Oracle”)

Con esta linea le especificamos al oracle que en el momento en el que ocurra un error ejecute la función SQL_ERROR. También le podíamos haber dicho que hiciera otras cosas como:

  • CONTINUE

  • GOTO etiqueta

  • Función

También se puede controlar con esta sentencia que cuando no halla error haga algo, cambiando SQLERROR por NOTFOUND después de la palabra WHENEVER.

El nombre de las variables que van a intervenir en las sentencias de SQL van siempre precedidas por el carácter `:'. A la hora de hacer un select tenemos que especificarle a donde van a ir a parar las filas que son el resultado. Para ello tenemos la palabra INTO, con el cual le especificamos a donde va a ir el resultado del select.

CURSORES

A la hora de hacer un select en el cual llegan muchas filas tenemos el objeto cursor.

#include <stdio.h>

EXEC SQL BEGIN DECLARE SECTION

CHAR USERNAME[7];

CHAR DNI[9]

FLOAT CUOTA;

EXEC SQL END DECLARE SECTION;

EXEC SQL DECLARE socios CURSOR FOR

SELECT cod_cli, cuota FROM clientes

ORDER BY cuota;

...

...

EXEC SQL OPEN socios;

FOR (I=1;I<=5;I++)

{

EXEC SQL FETCH socios INTO :cod_cli,:cuota

...

...

}

EXEC SQL CLOSE socios;

...

...

EXEC SQL COMMIT RELEASE

EXIT (0)

}

Se declara primero un cursor, con una select en concreto, y cuando se abre dicho cursor se ejecuta la select, pero sin hacer ninguna salida. Después tendremos que ir recuperándolas de una en una. El oracle tendrá todas las filas guardadas con un apuntador que se irá desplazando a medida que las vamos utilizando. Las filas se recuperan con la línea FETCH. Cada vez que se ejecuta una de estas sentencias el apuntador pasará a la siguiente fila a utilizar, y este apuntador no podrá volver atrás puesto que las líneas que ya se han recuperado son borradas de la memoria del oracle. Así que tenemos una estructura de recuperación de líneas que leerá las filas de resultado de las sentencias oracle de forma secuencial.

PRECOMPILACIÓN

Estos progamas huesped requieren de un programa precompilador que pasará el codigo del programa diseñado en ambos lenguajes (huesped y anfitrion) en el lenguaje del anfitrión para después poder compilarlo de forma normal y corriente. Así, los ejemplos anteriores pasarían a ser programas en C++, sustitullendo las funciones y llamadas de oracle por funciones de c equivalentes, para después poder usar el compilador de C++ en el codigo resultante. Estas partes de programas que interpreta el precompilador son llamadas implicitas a funciones de manejo del oracle.

PROGRAMACIÓN CON LLAMADAS EXPLICITAS

Una programación de este tipo es una programación en la que no hay llamadas a funciones de SQL, sino que directamente se usarían librerias para el manejo de la base de datos de oracle. De esta forma no sería necesario una precompilación del código puesto que el programa estaría totalmente escrito en un lenguaje anfitrión.

Para ello se usarían funciones especiales. La librería que tiene dichas funciones se llama OCI (Oracle Call Interface). Trabajar de esta forma es mucho más difícil y rebuscado que de la otra forma, puesto que tenemos que hacer muchas llamadas a funciones.

ODBC: Open DataBase Connectivity

Podemos hacer llamadas a funciones dinamicas del OBCD que serán resueltas con llamadas a las funciones de la base de datos. La ventaja de esto es que no se tiene que cambiar la codificación del programa para pasar de una base de datos a otra, es decir, que no importa que base de datos estemos utilizando por debajo del programa porque el codigo será siempre el mismo.

PL-SQL

Es un lenguaje que añade al SQL las características de un lenguaje procedimental . Las siglas son Procedural Lenguaje - SQL. Este lenguaje tiene que ser resuelto por el sistema gestor de base de datos, pero hay herramientas que tienen un motor de PL-SQL para que solo vaya al sistema gestor de base de datos los datos necesarios mientras que el programa hará otro tipo de procedimientos.

En este lenguaje tendremos:

  • BLOQUES ANONIMOS

  • TRIGGERS O DISPARADORES

  • PROCEDIMIENTOS

  • PAQUETES

En los tres últimos el código de PL-SQL está almacenado en la base de datos oracle, y el código de los bloques anónimos está en el fichero de texto que nosotros programemos. Para ello utilizaremos el SQL-Plus

ESTRUCTURA DE LOS PROGRAMAS PL-SQL

Un programa PL-SQL esta formado por bloques, y un programa ha de estar formado al menos por un bloque. La estructura de cada uno de estos bloques es de la forma siguientes, formado por tres párrafos:

[DECLARE

declaraciones;]

[BEGIN

sentencias;]

[EXCEPTION

excepciones;]

El único párrafo obligatorio es el de sentencias, y los demás son opcionales.

Cualquier programa de PL-SQL está formado por sentencias gramaticales que admiten los siguientes caracteres:

  • Las letras del alfabeto en mayusculas y minusculas

  • Digitos numéricos

  • Tab, esp, retorno de carro, ...

  • Signos aritméticos

  • Signos lógicos

  • (,),{,},[,],¿,!,...

IDENTIFICADORES

En el PL-SQL para identificar objetos se usan los identificadores que son un grupo de caracteres definidos de entre los admitidos. Estos deben empezar siempre por una letra y pueden continuar por más letra, dígitos numéricos, símbolo de dólar, símbolo almohadilla o símbolo _ . El máximo numero de caracteres es de 30. Hay una serie de palabras que no se pueden utilizar puesto que son palabras reservadas para el lenguaje PL-SQL como por ejemplo la palabra BEGIN.

DELIMITADORES

Son los caracteres que separan varibles y constantes para formar expresiones. Los admitidos por el PL-SQL son los siguientes:

ARITMETICOS:+, -, *, /, **

COMPARACION: <, >, <=, >=, <>, ¡=

DE INICION/FIN DE EXPRESON: (, )

DE TERMINACION DE ORDEN: ;

ATRIBUTO: %

DE CADENA DE CARACTERES: `'

SEPARADOR DE ELEMENTOS: ,

INDICADOR DE VARIABLES DE ASIGNACION: :

OPERADOR DE ASIGNACION: :=

CONCATENACION DE CADENAS: ||

COMENTARIO DE LINEA: --

COMENTARIO DE PARRAFO /* */

VARIABLES Y CONSTANTES

PL-SQL se comunica con la base de datos oracle por medio de variables y constantes. Estas se declaran en la parte de DECLARE del bloque que hemos visto antes. La estructura para declarar variables en PL-SQL es la siguiente:

DECLARE

Nombre_variable tipo [CONSTANTE][NOT NULL][:=valor (tambien DEFAULT valor];

Si una variable la definimos como constantes tendremos que asignarle una valor obligatoriamente y después no se podrá modificar dicho valor. Cualquier variable que se halla declarado pero que no se le halla dado un valor inicial tendrán el valor NULL. En el caso de que la declaremos como NOT NULL será obligatorio asignarle un valor a la hora de definirlo.

Posibles valores de las variables

  • BINARY_INTEGER: Este tipo de variable tendrá números enteros en el rango -2147483647 hasta 2147483647. Este tipo de dato admite también subtipos de datos:

  • NATURAL: Números naturales desde 0 hasta 2147483647

    POSITIVE: Números naturales estrictamente positivos: desde 1 hasta 2147483647

  • NUMBER(precisión, escala): Números en formato numero reales con el rango 1x10-130 hasta 9.99...x10 125 y -9.9...x10 125 hasta -1x10-130

  • CHAR (l): Almacena cadenas de caracteres de longitud fija. L establece la longitud de esa cadena, y se no se especifica l adquiere como longitud por defecto 1. En PL-SQL este tipo de dato admite hasta 32767 caracteres, mientras que en oracle solo podía ser de 256 caracteres. Esto quiere decir que no podremos meter en una columna char de oracle una de estas variables.

  • VARCHAR2(l): Almacena cadenas de caracteres de longitud variable de longitud máxima especificada por l. Es obligatorio especificar la longitud máxima. En PL-SQL la longitud máxima permitida es de 32767 mientras que en oracle el varchar2 podía almacenar hasta 2000 caracteres, con lo cual tenemos el mismo problema que antes. Hay dos subtipos de datos equivalentes a este tipo de dato que se llaman VARCHAR y STRING.

  • LONG: Contiene cadenas de caraceres de longitud variable de hasta 32760 caracteres. En el caso de que un campo long de oracle tenga un dato de más de 32760 caracteres no se podrá meter en una variable de este tipo.

  • RAW y LONG RAW: En el caso de RAW son cadenas de longitud variable de datos binarios de longitud l que admiten como máximo 32767. Seguimos teniendo el mismo problema que con el los tipos char. Con el LONG RAW contiene cadenas de caracteres variables de tipo RAW de hasta 32760 bytes. Seguimos teniendo el mismo problema que con el tipo de dato LONG

  • DATE: Es exactamente el mismo tipo de dato que el de oracle.

  • BOOLEAN: Solamente admite tres valores: TRUE, FALSE Y NULL. Suele aparecer en estructuras de control.

  • ROWID: Sirve para almacenar identificadores de filas. **????**

  • TABLE y RECORD

  • Atrituto type

    A la hora de declarar variables se pueden definir con el atributo %TYPE para declarar que dicha

    variable tiene las mismas características que otra variable:

    v_cod_usuario v_cod_cliente%TYPE;

    Para que esto sea admitido la variable de ejemplo tiene que estar definida antes de hacer esta declaración. También se puede hacer que una variable sea del mismo tipo de dato que una columna de una tabla de oracle a la que hagamos referencia:

    v_cod_cliente clientes.cod_cliente%TYPE;

    SENTENCIAS DE CONTROL

    En PL-SQL tenemos las sentencias de control de tipo repetitivas, alternativas y

    Sentencias alternativas

    Sentencia IF clasica:

    IF condicion THEN

    Sentencias;

    ...

    ...

    END IF;

    Sentencia IF alternativa:

    IF condicion THEN

    Sentencias;

    ...;

    ELSE

    Sentencias;

    ...;

    END IF

    Sentencias IF anidadas:

    IF condicion THEN

    Sentencias;

    ...;

    ELSIF condicion THEN

    Sentencias;

    ...;

    ELSE

    Sentencias;

    ...;

    END IF

    Sentencia NULL:

    Es una sentencia que no hace nada.

    Sentencias repetitivas:

    El bucle más sencillo y elemental es el LOOP:

    LOOP

    Sentencias;

    ...;

    ...;

    END LOOP;

    Este bucle sería infinito a no ser que en las sentencias le especifiquemos una sentencia de ruptura de bucle:

    LOOP

    Sentencias;

    EXIT;

    END LOOP;

    Si queremos que haya una condición de salida podríamos meter el EXIT dentro de un IF o podríamos usar la sentencia EXIT WHEN condición;

    También se pueden hacer bucles anidades, con lo cual necesitamos hacer sentencias EXIT a niveles. Para ello debemos etiquetar los diferentes bucles de un anidamiento. Para anidar un bucle se hace lo siguientes:

    <<etiqueta>>

    LOOP

    ...

    Un ejemplo de bucles anidados sería el siguiente:

    <<bucle1>>

    LOOP

    Sentencias;

    <<bucle2>>

    LOOP

    Sentencias;

    EXIT bucle2 WHEN condicion;

    Sentencias;

    END LOOP bucle2;

    Sentencias;

    END LOOP bucle1;

    Otro tipo de bucle es el WHILE:

    WHILE condicion LOOP

    Sentencias;

    ...;

    END LOOP;

    Las sentencias se ejecutaran mientras la condición sea cierta.

    Otra estructura repetitiva es el FOR:

    FOR indice IN [REVERSE] valor_inicial..valor_final LOOP

    Sentencias;

    ...;

    END LOOP;

    Se repiten las sentencias que hay dentro del for por cada unidad que se pasa desde le valor inicial hasta el valor final. No existe ninguna clausula para que avance de varios en varios. Si se le pone la clausula reverse los valores inicial y final se tomaran al contrario, es decir, que el menor siempre estará en el inicial y el mayor en el final, o de lo contrario da error.

    Sentencias de control de flujo

    Sentencia GOTO: Esta sentencia permite bifurcar el flujo del programa a otra posición que tiene una etiqueta.

    GOTO etiqueta;

    Después de la etiqueta tiene que haber una sentencia ejecutable. No se puede hacer un goto a una etiqueta que tenga justamente después el final del programa. Para ello tenemos el NULL.

    CURSORES

    Declaración de cursores

    Para declara un cursor se hace lo siguiente:

    DECLARE

    CURSOR nombre_cursor IS SELECT ....

    Ejemplo:

    CURSOR c1 IS SELECT apellidos, nombre FROM CLIENTES

    En este momento solo se asocia el nombre de un cursor a una sentencia, pero en ese momento la sentencia SQL todavía no se ha ejecutado.

    Ejecución de un cursor:

    Esto se hace dentro del BEGIN. Se ejecuta de la siguiente forma:

    OPEN nombre_cursor;

    Es en este momento en el que se ejecuta la cosulta asociada a dicho cursor.

    Consulta de los datos almacenados en el cursor:

    Para hacer la consulta primero tenemos que haber abierto el cursor del que queremos obtener los datos. La recuperación de los datos se hace de la forma siguiente:

    FETCH nombre_cursor INTO variable1, variable2, ....

    Las variables hay que ponerlas en el mismo orden en el que se han recuperado las colunas. Habra que meter esta sentencia en una estructura repetitiva para que se controle que se metan todos los datos, o que se vean todos los datos.

    Cerrar un cursor:

    Una vez que hemos leido todas las filas del cursor se cierra:

    CLOSE nombre_cursor;

    Cuando se declara un cursor en la sentencia SQL puede que aparezcan variables, por ejemplo en la cláusula WHERE, para obtener diferentes resultados según un parámetro. Para ello, cuando se abre el cursor, se tendrá en cuenta el valor de las variables que se han puesto en el cursor. El orden de acciones que lleva a cabo es el siguiente:

  • Sustitución de variables

  • Envía la sentencia SQL a ORACLE

  • CURSOR c1 IS SELECT cod_cliente FROM clientes WHERE cuota>v_cuota;

    Cuando se declara un cursor se puede al mismo tiempo declarar las variables que utiliza el mismo cursor:

    DECLARE

    CURSOR nombre_cursor [variable tipo [:=valor]] IS SELECT ....

    Ejemplo:

    CURSOR c1 (cuota_minima NUMBER) IS SELECT descripcion FROM actividades WHERE cuota > cuota_minima;

    Después a la hora de abrir el cursor se tendrá que incluir los valores para esas variables:

    OPEN c1 (5000);

    En el caso de que sean varias las variables los valores en la llamada irán separados por `,'.

    Atributos

    Hay cuatro atributos diferentes cuando se trabaja con cursores y que podemos utilizar:

    %NOTFOUND: Este cursor devuelve el valor TRUE cuando al ejecutar una FECTH no se ha encontrado ninguna fila en el resultado. Será falso si el último FETCH ha devuelto una fila. Será NULL cuando se ha hecho un OPEN al cursor pero todavía no se ha hecho ningún FETCH. La sintaxis es:

    nombre_cursor%NOTFOUND

    Es util para controlar el bucle de lectura de los registros del resultado de un cursor.

    LOOP

    FETCH nombre_cursor INTO variables;

    Sentencias;

    EXIT WHEN nombre_cursor%NOTFOUND;

    END LOOP;

    %FOUND: Este atributo devolverá TRUE cuando el último FETCH ha encontrado una fila como resultado y será FALSE cuando no haya devuelto ninguna fila como resultado. Devolverá NULL cuando no se halla ejecutado ningún FETCH desde el último OPEN del cursor que ha sido ejecutado.

    LOOP WHILE nombre_cursor%FOUND;

    FETCH nombre_cursor INTO variables;

    Sentencias;

    END LOOP;

    %ROWCOUNT: Este atributo devuelve el numero de filas recuperadas por el FETCH. Sirve para recuperar un numero de filas en concreto. Este número se irá incrementando cada vez que se ejecuta un comando FETCH.

    LOOP

    FETCH nombre_cursor INTO variables;

    Sentencias;

    EXIT WHEN nombre_cursor%ROWCOUNT > 100;

    END LOOP;

    %ISOPEN: Este atributo nos devuelve TRUE si el cursor está abierto y FALSE si está cerrado.

    Ejercicios:

  • Actualizar el nombre del cliente , `A1111' a Antonio José

  • Actualizar la cuota del cliente `A1111' incrementándosela en un 10% si la antigüedad es mayor de 2 años o un 8% si es mayor de tres años o un 5% en otro caso.

  • <<<AMPLIACION>>>

    El campo ROWID sierve como identificativo de cada fila de una tabla. Tiene el formato:

    ROWID XXXXXXXX.YYYY.ZZZZ

    XXXXXXXX Es el numero del datablock en la que está metida dicha fila dentro de un tablespace

    YYYY Es el numero de fila que ocupa en el datablock de un tablespace concreto

    ZZZZ Es el identificativo del fichero en el que está metido dicho datablock

    Cursores implicitos

    Cada vez que nosotros hacemos un SELECT sin usar ningún cursor, en realidad el SQL esta creando un cursor. De esta forma, cuando nosotros sabemos que el resultado es un unico valor y lo introducimos en una variable podemos usar las propiedades de los cursores, con la excepción de que no hay nombre y tendremos que usar las propiedades de la siguiente forma:

    SQL%NOTFOUND

    Un cursor de este tipo nunca está abierto, porque cuando se ejecuta una sentencia de este tipo hace el solo las sentencias OPEN, FETCH y CLOSE en la misma sentencia, y por lo tanto no se podrá usar la propiedad ISOPEN puesto que siempre va a estar cerrado.

    Cláusula WHERE CURRENT OF

    Con esta clausula nos podemos referir a la fila actual que hay en el fetch para poder modificar los valores de dicha fila.

    DECLARE

    V_cod_cliente VARCHAR2(2);

    CURSOR C1 IS SELECT cod_cliente, nombre FROM CLIENTES

    WHERE cod_cliente = `A1111'

    V_nombre VARCHAR(20);

    BEGIN

    OPEN c1;

    LOOP

    FETCH c1 INTO v_cod_cliente, v_nombre;

    UPDATE clientes SET v_nombre='Pedro' WHERE CURRENT OF c1;

    END LOOP;

    END;

    REGISTROS DE DATOS RECORD Y TABLE

    Tablas:

    Una tabla es un conjunto de datos de tipo uniforme y los datos van referenciados por un indice que es de tipo binary_integer. Para hacer se declara un tipo de tabla y una vez hecho esto se declara una tabla asociada a ese tipo de dato de tabla.

    Declarar un tipo de tabla se hace de la siguiente forma:

    TYPE tipo_tabla IS TABLE OF {tipo_dato|variable%TYPE | table.columna%TYPE } [NOT NULL] INDEX BY BINARY_INTEGER

    Después se puede declarar una o varias tablas con el tipo de dato que hemos definido.

    Nombre_tabla tipo_tabla

    El tamaño máximo de la tabla viene dado por el numero máximo de ocurrencias que puede tener el índice. El espacio que ocupa una tabla en memoria es dinamico, es decir, que va aumentando según le vamos metiendo datos.

    Registros:

    Es un conjunto de campos que no son necesariamente del mismo tipo de datos a los que nos podremos referir en conjunto. Un ejemplo de declaración de registro es el siguiente:

    TYPE tipo_registro IS RECORD

    ( campo1 {tipo_dato | variable%TYPE | tabla.columna%TYPE}

    campo2 { “ }

    ...

    )

    Después, para crear un registro se hace lo siguiente:

    Nombre_registro tipo_registro;

    Hay un atributo que se llama ROWTYPE que permite crear un registro con los tipos de datos de una tabla. De esta forma tendriamos un registro que tiene los mismos datos que una tabla.

    Nombre_registro tabla%ROWTYPE

    INSTRUCCIÓN FOR PARA REGISTROS

    FOR nombre_registro IN nombre_cursor LOOP

    ...

    ...

    END LOOP

    Con esta instrucción no tenemos que preocuparnos de abrir el cursor porque lo hace el mismo y en cada pasada se encarga de recuperar la fila siguiente del cursor.

    Se puede romper la estructura repetitiva con la sentencia EXIT.

    EJERCICIOS:

  • Crear un programa SQL que actualice las cuotas de los clientes activos con el criterio:

    • Antigüedad menor de 2 años a 10%

    • Antigüedad menor de 3 años y mayor de 2 a 8%

    • Antigüedad mayor de 3 años a 5%

  • Crear una tabla como la de clientes. Construir el PL/SQL que carge en la nueva tabla las filas de la tabla clientes.

  • Para mantener los saldos de la tabla de prestamos de empleados.

  • Preparar un programa para mantener la tabla de pagos de prestamos.

  • Crear una tabla HIST_INCR de histórico de incrementos en la aplicación de GIMNASIOS

  • Columnas:

    Cod_cliente

    Fecha_incremento

    Porc_incremento

    Cuota_previa

    (hija de clientes)

    Preparar un PL/SQL que incremente la cuota de los clientes del gimnasio que tienen actividad `Gimnasia de mantenimiento' en: 5% si solo tienen esa actividad, 3% si tienen alguna actividad más. Y mantener la tabla HIST_INCR

    Transacciones

    Es un conjunto de operaciones que se trataba como un todo. Son un conjunto de operaciones que no son operativas hasta que se ejecuta un commit. Funcionan de una manera de todo o nada. Una transacción comienza cuando termina la anterior o cuando comienza el programa o bien después de una sentencia de definición de datos. El final de una transacción se sucede cuando se ejecuta un commit y además los cambios son confirmados en la base de datos; cuando se ejecuta la instrucción rollback, momento en el que los cambios no se confirman; cuando se codifica un comando DDL; o cuando se finaliza el programa.

    En un programa PL/SQL se asume por defecto que si el programa acaba con éxito se ejecute la sentencia commit, y en el caso de que se produzca un error se ejecute la sentencia rollback. También se puede poner a lo largo del programa un savepoint en el cual se van a salvar los datos hasta ese punto en caso de que se suceda un error en el resto del programa.

    La forma de usar estos puntos seguros es especificando en cualquier punto del programa:

    SAVEPOINT identificativo;

    Después se puede especificar un rollback to identificativo, con lo cual se ejecutaría un rollback hasta el savepoint que hemos especificado. Si ejecutáramos un Rollback se desharía toda la transacción.

    SET TRANSACTION

    Es una sentencia que tiene que ser la primera de un programa PL/SQL. Con esta sentencia se puede codificar que una transacción sea solo de consulta: (SET TRANSACTION READ ONLY). Con esta cualidad además el programa tendrá integridad de lectura, esto quiere decir que al principio de un programa se guardaran los datos que se van a usar, para que si cualquiera de las tablas es modificada durante la ejecución por otro usuario, nosotros trabajemos con las originales del principio del programa.

    También nos permite usar segmentos de rollback especificos (SET TRANSACTION USE ROLLBACK SEGMENT nombre_segmento) Esto sirve para especificar segmentos más grandes para el caso de que el segemento por defecto que nos asigna oracle se quede pequeño.

    Apartado EXCEPTION

    Sirve para codificar programa en el caso de que se produzca un error durante la ejecución del apartado BEGIN. Cuando ocurre un error en el programa lo primero que hace es ir a la sección de exception para ver si hay una rutina para dicho error.

    WHEN no_data_found THEN

    ...

    Cuando ocurre un error se acabaría el bloque begin de esa excepción y pasaría al anterior en el caso de que existiera. Si no existe se acaba el programa. Si no encuetra ese error en le bloque exceptión de ese bloque pasará al del bloque anterior hasta que le encuentre o hasta que se termine el programa.

    Algunos errores son:

    • CURSOR_ALREADY_OPEN

    • DUP_VAL_ON_INDEX

    • INVALID_CURSOR

    • NO_DATA_FOUND

    • TOO_MANY_ROWS

    • ZERO_DIVIDE

    • NOT_LOGGED_ON: cuando se ha perdido la conexión con el oracle

    • PROGRAM_ERROR: error interno del programa.

    • TIMEOUT_ON_RESOURCE: cuando no se puede coger un recurso porque le tiene cogido otro usuario.

    Se puede usar la clausula OTHER para indicar el caso de que ocurra cualquier error que no este expecificado.

    CONTROLES DE ERRORES

    Nosotros podemos declarar también un control de error exception

    DECLARE

    ...

    ...

    cuota_mala EXCEPTION;

    ..

    BEGIN

    ...

    ...

    if registro_cuota IS NULL THEN

    RAISE cuota_mala;

    ...

    EXCEPTION

    ...

    WHEN cuota_mala THEN

    Sentencias;

    También podemos asociar un error del Oracle a un error del PL/SQL con la instrucción:

    PRAGMA EXCEPTION INIT (nombre excepcion, numero de error)

    Ejemplo: PRAGMA EXCEPTION INIT (segment_rollback_lleno,-1626)

    Y después podremos preguntar por el en la sección de EXCEPCIONES.

    Variables de errores

    Oracle devuelve dos tipos de variables cuando se produce un error

    SQLCODE: devuelve el código del error que se ha producido en el oracle

    SQLERRM: devuelve el mensaje de error.

    Nosotros podemos usar esas dos variables en la sección de EXCEPIONES. En el caso de que esté controlada en la zona de OTHER (en la que se controlan todos los errores no especificados) se podrá usar la variable para saber que error es.

    Subprogramas en PL/SQL

    El Pl/SQL va a tener dos tipos de subprogramas: procedimientos y funciones. Un procedimiento es una parte de programa que realiza una serie de operaciones y una función es un subprograma que ejecuta una serie de operaciones y que devuelve un valor. Para definir un procedimiento se hace lo siguiente:

    PROCEDURE nombre_procedimiento [listado parametros] IS

    [Declaraciones;]

    BEGIN

    Sentencias;

    EXCEPTION

    Sentencias;

    END;

    La declaración de un procedimiento tiene dos partes, la primera que se llama zona de especificaciones, que es donde aparecen el nombre del procedimiento y la lista de parámetros, y después está el cuerpo del procedimiento. Para invocar a un procedimiento se pone el nombre del procedimiento y después entre paréntesis la lista de parámetros. Antes de poder invocar a un procedimiento este tiene que estar declarado. Un procedimiento puede estar archivado en la propia base de datos, lo que da a un nuevo objeto de oracle que se llama procedimiento.

    La lista de parámetros, cada parámetro, esta compuesta por el nombre del parámetro y el tipo de parámetro y a continuación indicar cual es el tipo de dato. Después se le puede asignar un valor por defecto. Un parámetro es de tipo IN cuando se le pasa al procedimiento y este no le puede modificar, porque el procedimiento lo trata como una constante. Este es el tipo de parámetro por defecto. El tipo de parámetro OUT es un parámetro que te va a devolver un valor del procedimiento. Un parámetro de tipo IN OUT es un parámetro de los que se le pasa al procedimiento con un valor y te puede devolver otro valor. Cuando se le especifica el tipo de dato no se puede especificar la longitud del dato.

    En una base de datos podemos crear procedimientos. Esto va a ser un programa PL/SQL que está guardado dentro de la base de datos de Oracle. Para crear un procedimiento dentro de una base de datos usaremos la sentencia siguiente:

    CREATE PROCEDURE nombre del procedimiento [listado de parámetros] AS

    [Declaraciones;]

    BEGIN

    Sentencias;

    [EXCEPTION

    Sentencias;]

    END;

    Para consultar los procedimientos que tenemos, los podemos consultar en la tabla USER_SOURCE. Si queremos que otro usuario pueda usar nuestros procedimientos tenemos que darle el privilegio execute:

    GRANT EXECUTE ON nombre_procedimiento TO usuario.

    Funciones

    Para invocar a una función tendrá que estar metida dentro de una expresión. Para definir una función se hara lo siguiente:

    FUNCTION nombre_función (lista de parámetros) RETURN tipo_dato IS

    [Declare local]

    BEGIN

    Sentencias;

    [EXCEPTION

    sentencias;]

    END;

    En este caso tampoco hay que especificar la palabra clave declare. La lista de parámetros de las funciones tiene la misma estructura que la de las funciones.

    Las funciones también son un objeto de oracle que se pueden insertar en la base de datos oracle. Para crear una función existe el comando

    CREATE [OR REPLACE] FUNCTION nombre_función (lista de parámetros) RETURN ...

    Para dar privilegios sobre las funciones a otros usuarios se hace lo mismo que con los procedimientos.

    PACKAGES

    Es un objeto de la base de datos que permite agrupar de una forma lógica procedimientos, funciones, cursores, declaraciones de tipo de variable, declaraciones de excepciones, etc...todo aquello que se pueda imaginar que forma parte del desarrollo de una aplicación. Agrupar todo esto en un paquete facilita el hecho de simplificar la gestión de los permisos

    ALTER PROCEDURE nombre COMPILE;

    ALTER FUNCTION nombre COMPILE;

    ALTER PACKAGE nombre_paquete COMPILE;

    Oracle puede leer de una sola vez diversos objetos que hay dentro de un paquete, con lo que aumenta el rendimiento de la aplicación.

    Un package tiene dos partes: una que son las expecificaciones del paquete y otra que es el cuerpo del paquete. Cuando se crea el cuerpo del paquete se define el cuerpo de los procedimientos (o cualquier otro objeto) que hay en el paquete. En las expecificaciones se definen que objetos hay.

    CREATE [OR REPLACE] PACKAGE nombre IS

    Declaraciones de tipo;

    Declaraciones de variables;

    Declaraciones de constantes;

    Declaraciones de excepciones;

    Declaraciones de cursores;

    Declaraciones de subprogramas (procedimientos y funciones)

    END nombre_paquete;

    EJEMPLO DE CREACION DE PAQUETE

    CREATE PACKAGE emp_activ AS

    FUNCTION create_dept (dname VARCHAR2, loc VARCHAR2) RETURN number;

    PROCEDURE remove_emp (no_emp NUMBER);

    PROCEDURE remove_dept (no_dept NUMBER);

    PROCEDURE incr_sal (no_emp NUMBER, sal_incr NUMBER);

    No_comm EXCEPTION;

    No_sal EXCEPTION;

    END emp_activ;

    Paquetes por defecto

    DBMS_OUTPUT: Tiene una series de subprogramas de los cuales uno de ellos permite mandar mensajes al usuario por pantalla desde el programa PL/SQL: PUT y PUT_LINE

    PUT permite mandar un mensaje por pantalla al usuario pasándole una variable de tipo char. PUT_LINE: Para invocar a este procedimiento es pasarle como parametro una variable de tipo character. En realidad envía el texto a un buffer intermedio. Cuando estemos utilizando SQL Plus y ejecutamos este procedimiento esos mensajes nos los escribe a la pantalla cuando se finaliza el programa PL/SQL. No escribe el mensaje cada vez que se hace una llamada al procedimiento. Para que esto sea así hay que activar a On una variable del Oracle llamada SERVEROUTPUT.

    DBMS_STANDARD

    RAISE_APLICATION_ERROR: Esta función tiene una serie de parametros que nos permiten activar errores de usuario.

    Disparador o Trigger

    Es un objeto, que es un conjunto de PL/SQL, que se ejecuta inmediatamente después de ejecutar una instrucción de la base de datos como puede ser un INSERT o un DELETE. Cuando se ejecuta dicha instrucción se dispara el trigger (de ahí viene su nombre). La sintaxis para crear un disparador es la siguiente:

    CREATE [OR REPLACE] TRIGGER nombre {BEFORE|AFTER} {DELETE [OR INSERT][OR UPDATE[OF columnas]]} ON tabla [REFERENDIG {OLD AS | NEW AS } [FOR EACH ROW] [WHEN condición]

    DECLARE

    BEGIN

    END;

    Puede haber disparadores de dos tipos: Hay disparadores que se ejecutan una vez por cada fila que se produce (Triger a nivel de fila), o de los que solo se ejecuta una vez para todas la filas (Trigger a nivel de sentencia). Para especificar que se ejecute para cada fila se pone la expresión FOR EACH ROW.

    La condición limita el rango de filas para las cuales se ejecuta el trigger. Si queremos que la condición sea a valores de antes de modificarse la fila tendremos que especificar old antes de la columna, y new para decirle que sea el valor después de modificar la fila. La clausula REFERENDING sirve para renombrar las palabras old o new por cualquier otra palabra.

    EJERCICIOS:

  • Borrar las filas de CLIENTES2. Crear un procedimiento que copie desde CLIENTES los N clientes de mayor cuota. N será un parámetro pasado al procedimiento. Contemplar el caso que haya más de un cliente con la misma cuota en cuyo caso si aparece uno debe aparecer el otro. Preparar un PL/SQL que ejecute el procedimiento para un caso concreto.

  • Crear una tabla de mensajes con dos columnas ID_mensaje, mensaje de tipos number y varchar2(2000) para introducir mensajes de las aplicaciones. Preparar un PL/SQL para recuperar todos los departamentos y empleados de cada departamento, introduciendo los resultados en la tabla mensajes. Utilizar para ello dos cursores en lugar de un JOIN entre las dos tablas.

  • Tratamiento de excepciones. Preparamos un PL/SQL para seleccionar el sueldo concreto de un empleado. Le pedimos este sueldo a través de una variable de usuario de SQL*Plus (utilizar un accept/prompt):

    • Si hay más de un empleado con el sueldo utilizar a través de una rutina de excepciones, un mensaje `Mas de un empleado con salario: <salario>.

    • Si no hay ninguno, tratar el error e introducir un mensaje en tal sentido

    • Sí hay un empleado con ese sueldo introducir en la tabla de mensajes el nombre del empleado y su salario.

    • Gestionar cualquier otro error introduciendo un mensaje del tipo `Se ha producido algún otro error'

  • Tratamiento de excepciones de usuario. A partir de un salario introducido por teclado por el usuario y almacenado en una variable de usuario (utilizar ACCEPT/PROMPT) escribir un bloque PL/SQL que nos diga cuantos empleado tienen un salario mayor o menor a 20000 el salario introducido por el usuario. De tal forma que si no hay ninguno le muestre un mensaje indicándole tal condición, controlando esta situación con una excepción. Si hay uno o más empleados mostrar un mensaje indicándole al usuario el número de empleados, controlando esta situación con una excepción también. Y por último controlar cualquier otro tipo de error con otro mensaje.

  • Manejo de funciones. En la aplicación de empleados crear una función que devuelva un mensaje al usuario con la descripción de un Departamento cuando se la pasa el código de un Departamento, controlar con una excepción la posibilidad de que el departamento no exista con un mensaje al usuario. Preparar posteriormente un PL/SQL que utilice la función a partir de un código de departamento introducido por el usuario.

  • Se trata de hacer una programación con PL/SQL, utilizando las tablas de la aplicación de empleados , para realizar el cálculo de la nómina para los empleados de la empresa ficticia cuyos datos se soportan en estas tablas. No se trata de un cálculo de nómina real y algunos supuestos no se ajustan a ninguna normativa legislativa laboral o fiscal existente.

  • 1.- Vamos primero a realizar algunas adaptaciones de los datos en las tablas:

    1.1.- En la columna salario_base debe aparecer el salario anual. Luego multiplicar por 14 el valor de esta columna para convertir salario mensual en salario anual.

    1.2.- Modificar en prestamo_empleado la columna de interés para los prestamos activos con los siguientes criterios:

    • Prestamo Ordenador y Electrodomésticos : Interés de 2%

    • Prestamo Vivienda: 2,5%

    • Prestamo Automóvil : 3%

    Nota: La fórmula para calcular el interés compuesto es p*(1+i/100)**n. Donde la cantidad prestada es : p , el interés aplicado es: i, y el número de años es :n

    1.3.- Modificar la tabla de Categorías para recoger las siguientes:

    • Cat 1: Directivo

    • Cat 2: Analista Funcional

    • Cat 3: Programador.

    • Cat 4: Técnico de Explotación.

    • Cat 5: Tecnico de Sistemas.

    1.4.- Modificar los empleados para que tengan este nuevo rango de categorías.

    1.5.- Crear una tabla con la codificacion de horas extraordinarias:

    • Cod_extr number(1)

    • Descripción varchar2(15)

    Cargarla con los siguientes datos : (1,Estructurales),(2,Requerimientos), (3,Urgentes)

    1.6.- Crear una tabla de valoración de horas extraordinarias (debe ser hija de la tabla anterior):

    • Cod_extr

    • Cod_cat

    • Valoracion number(4)

    Cargarla con los siguientes datos:

    INSERT INTO xxxxxx VALUES (1,2,4000);

    INSERT INTO xxxxxx VALUES (2,2,4500);

    INSERT INTO xxxxxx VALUES (3,2,5000);

    INSERT INTO xxxxxx VALUES (1,3,3000);

    INSERT INTO xxxxxx VALUES (2,3,3500);

    INSERT INTO xxxxxx VALUES (3,3,4000);

    INSERT INTO xxxxxx VALUES (1,4,3000);

    INSERT INTO xxxxxx VALUES (2,4,3500);

    INSERT INTO xxxxxx VALUES (3,4,4000);

    INSERT INTO xxxxxx VALUES (1,5,4000);

    INSERT INTO xxxxxx VALUES (2,5,4500);

    INSERT INTO xxxxxx VALUES (3,5,5000);

    1.7.-Crear una tabla para recoger el número de horas extraordinarias realizadas por los empleados (esta tabla de be ser hija de la tabla de empleados y de la tabla de Códigos de Horas extraordinarias):

    • Cod_empl

    • Cod_extr

    • Mes

    • Año

    • Nº de horas extraordinarias

    Cargarla con los siguientes datos:

    INSERT INTO xxxxxx VALUES (1,2,12,1999,10);

    INSERT INTO xxxxxx VALUES (1,3,12,1999,3);

    INSERT INTO xxxxxx VALUES (4,2,12,1999,28);

    INSERT INTO xxxxxx VALUES (5,3,12,1999,10);

    INSERT INTO xxxxxx VALUES (8,1,12,1999,15);

    INSERT INTO xxxxxx VALUES (9,2,12,1999,40);

    INSERT INTO xxxxxx VALUES (12,3,12,1999,10);

    INSERT INTO xxxxxx VALUES (6,3,12,1999,6);

    INSERT INTO xxxxxx VALUES (7,1,12,1999,18);

    1.8.- Crear una tabla para recoger los gastos de viaje que se han podido generar (debe ser hija de la tabla de empleados):

    • Cod_empl

    • Fecha_viaje

    • Importe Gastos

    Cargarla con los siguientes datos

    INSERT INTO xxxxxx VALUES (1,to_date('dd/mm/yyyy','12/12,1999'),25344);

    INSERT INTO xxxxxx VALUES (3,to_date('dd/mm/yyyy','4/12,1999'),32123);

    INSERT INTO xxxxxx VALUES (6,to_date('dd/mm/yyyy','18/12,1999'),143533);

    INSERT INTO xxxxxx VALUES (1,to_date('dd/mm/yyyy','21/12,1999'),25344);

    INSERT INTO xxxxxx VALUES (8,to_date('dd/mm/yyyy','15/12,1999'),54321);

    INSERT INTO xxxxxx VALUES (11,to_date('dd/mm/yyyy','3/12,1999'),13765);

    INSERT INTO xxxxxx VALUES (8,to_date('dd/mm/yyyy','3/12,1999'),52123);

    INSERT INTO xxxxxx VALUES (10,to_date('dd/mm/yyyy','14/12,1999'),132324);

    INSERT INTO xxxxxx VALUES (2,to_date('dd/mm/yyyy','2/12,1999'),21005);

    1.9.- Crear una tabla de resultados de nómina (que será hija de la tabla de empleados):

    Cod_empl

    Fecha_nómina

    Mes_nom

    Ano_nom

    Salario

    Antig

    Gast_viaje

    Hor_extr

    Ret_IRPF

    Ret_seg_soc

    Ret_paro

    Ret_prest

    2.- Vamos a realizar algunas consideraciones que serán necesarias para realizar el cálculo.

    2.1.- El porcentaje de retención de IRPF para un trabajador se calculará en función de su salario anual: por cada 220000 ptas que supere el 1500000 de salario anual se le retendrá un punto con una límite del 56%. Este porcentaje se aplicará sobre la suma de todos los conceptos excepto sobre los Reembolsos por Pagos de Gastos de Viaje que no tienen retención.

    2.2.- El porcentaje de contribución a la Seguridad Social será fijo para todos los trabajadores: 4,7%. Se aplica exclusivamente sobre el concepto salario.

    2.3.- El porcentaje de retención para Desempleo y Formación Profesional, será fijo para todos los trabajadores: 1,7%

    2.4.- Los trienios en esta empresa se pagan a 3535 ptas de manera lineal a todos los trabajadores. Los trienios se cuentan por años naturales.

    2.5.- El número de horas extraordinarias por trabajador y mes no pueden superar las 25 horas. Sí se superan se traslada el exceso al mes siguiente para proceder a su pago en la próxima nómina.

    3.- Se trata con este ejercicio de hacer un programa que calcule la nómina de los empleados de esta empresa ficticia. Utilizar para ello todos los procedimientos y funciones que se estimen necesarios. En el cálculo de la nómina se han de tener en cuenta los siguientes puntos:

    • Pago de Salario.

    • Pago de horas Extraordinarias. Con sus restricciones.

    • Pago de Antigüedad (trienios).

    • Pago de Gastos de Viaje.

    • Retenciones por IRPF.

    • Retenciones por pagos a la Seg. Social y Desempleo y Formación Profesional

    • Retenciones por pagos de prestamos activos.

    Y tener en cuenta la siguiente consideración:

    • El cálculo de la nómina tiene que hacerse de tal forma que sí se produce un error en el cálculo de la nómina de un empleado, se debe dar por válido el cálculo hasta el empleado inmediatemente anterior, no considerar lo realizado para el empleado con problemas y que el programa se pueda rearrancar desde el cálculo del empleado donde nos canceló la última vez.

    Se precisan obtener los siguientes productos:

    3.1.- EL cálculo de la nómina de los empleados. Los resultados de nómina se irán recogiendo en la tabla de Resultados de Nómina creada en el apartado 1.9.

    3.2.- Un informe con el importe de la retención aplicada para Hacienda. Este informe irá desglosado por empleados (nombre, dni, importe de la retención) con un total de la retención aplicada en conjunto. Informe para Hacienda.

    3.3.- Informe para la Seg. Social con el total de lo retenido en la empresa por los dos conceptos.

    3.4.- Informe de Horas extraordinarias. Se deberá incluir una línea con el total de horas de cada tipo por categoría. Y una suma total de horas de cada tipo. En el informe aparecerá, sí se dá el caso, el nombre y númerod e horas de exceso, el empleado que superó el número de horas máximo por mes.

    3.5.- Informe de transferencia bancaria. Un informe con el total de la nómina. Esta información será utilizada para la transferencia bancaria.

    3.6.- Un informe con la nómina de los empleados. Con una cabecera con los datos del empleado y posteriormente los diferentes conceptos de la nómina.

    Para todos estos informes utilizar la tabla de Mensajes a la que se añadirá una nueva columna para poder identificar el Informe. Así cada línea deberá tener un identificativo de Informe un número consecutivo dentro del informe y la información propiamente dicha del Report. Ir dejando las líneas de los diferentes informes en esta tabla para posteriormente con SQL*PLUS poder extraerlos.

    Desarrollo de Aplicaciones en lenguajes de 4º generación 2º Evaluación

    1

    21