Story Transcript
Estructura de Datos y de la Información II SQL estático y dinámico: Dentro del SQL podemos encontrar dos variantes de éste, el SQL estático y el dinámico. Podríamos describir al SQL estático como aquella parte del mismo que se ocupa de consultar, modificar, etc una base de datos en la cual se puede decir que la forma de actuar es siempre la misma; las tablas y columnas referenciadas son, por tanto, conocidas de antemano por el programador, lo que le lleva a crear una serie de pasos comunes a seguir. Esto supone que si en algún momento fuese el propio programa creado el que tuviese que decidir, en tiempo de ejecución, las sentencias a usar o las tablas a referenciar, este tipo de SQL nos sería completamente inútil. Para ello, se crea lo que se conoce como SQL dinámico, capaz de no codificar de forma fija una sentencia de SQL inmerso en un código fuente. Por su parte, hace que sea el programa el construya dicha(s) sentencia(s), en una de sus áreas de datos y en tiempo de ejecución, y luego traspase el texto de la(s) misma(s) a la base de datos para que se ejecute en tiempo real. Sentencias SQL ejecutables: Todas las sentencias de las que consta el SQL pueden ser ejecutadas desde un programa de aplicación, como por ejemplo el C, Ada, PL/1, Fortran, Cobol, ... Al igual que en cualquier otra aplicación, lo primero es definir las variables que se vayan a usar. Para ello el SQL inmerso hace uso de la sentencia BEGIN DECLARE SECTION (precedida por el prefijo EXEC SQL comentado posteriormente) y que, como ya hemos dicho, indica el comienzo de una sección en la que se declaran las variables dentro del lenguaje que soporte al SQL. Generalmente, las variables aquí contenidas son simples (no estructuras compuestas), no deben contener redefiniciones y no se deben definir arrays, aunque en C se permiten los arrays de caracteres para la manipulación de cadenas. NOTA: pueden existir varias partes de definición de variables. Cuando ya hayamos definido las variables que usaremos posteriormente, hay que indicarle al programa que hemos acabado dicha parte de definiciones. Esto lo haremos poniendo la sentencia END DECLARE SECTION (igual que la sentencia anterior debe ir precedida por el prefijo EXEC SQL) e indica el final de una sección en la que se han declarado las variables a usar. Una vez definidas las variables que usaremos, lo siguiente es incluir las sentencias del SQL ejecutables dentro de dicho lenguaje. Para ello antepondremos el prefijo EXEC SQL a todas las sentencias, que indica comienzo de una sentencia SQL ejecutable. Dicha sentencia podrá ir a continuación de dicho prefijo o en una nueva línea. Ejemplo: Queremos que al ejecutar un determinado programa (en este caso un programa C, Ada o PL/1) haya una sentencia SQL ejecutable que borre todas las filas de una tabla denominada ALUMNO. EXEC SQL DELETE FROM ALUMNO; Como vimos anteriormente, el SQL dinámico resulta mucho más útil en lo que a uso del código se refiere. Por 1
ello, considero de suma importancia el explicar las sentencias más comunes y usadas del mismo. La forma más sencilla de SQL dinámico es la que nos proporciona la sentencia EXECUTE INMEDIATE, que prepara y ejecuta inmediatamente una sentencia SQL encontrada en la variable−host especificada o en una cadena−literal. Su sintaxis es la siguiente: EXECUTE INMEDIATE {:variable−host | cadena−literal} NOTA: Como ya hemos dicho, el prefijo EXEC SQL debe preceder a todas las sentencias ejecutables. A partir de ahora nos ahorraremos el especificarlo ya que lo consideraremos por supuesto. NOTA: A la hora de dar sintaxis del SQL admitiremos algunos convenios sintácticos: Convenio MAYÚSCULAS Cursiva Corchetes []
Llaves {} Puntos , ...
Significado Indican palabras clave y reservadas que deben usarse tal y como aparecen; aunque existen algunas implementaciones del SQL que admiten minúsculas. Representan variables definidas por el usuario Encierran elementos separados por el carácter | si son más de uno, e indican escoger uno o ninguno de dichos elementos; pero no hay que ponerlos a la hora de la implementación. Igual que el anterior, pero con la diferencia de que aquí sepuntos debe escoger un elemento obligatoriamente. Estos suspensivos indican que se puede repetir un elemento una o más veces. Cuando aparece una coma con los puntos es necesario poner una coma entre los elementos.
Esta sentencia EXECUTE INMEDIATE tiene, entre otras, las siguientes condiciones: • Debe ir seguida por su delimitador apropiado (que ya veremos más adelante). • La sentencia ejecutada no puede ser un SELECT. • En la mayoría de las implementaciones no se puede ejecutar directamente las proposiciones OPEN o CLOSE. Algunas tampoco permiten DECLARE CURSOR, DESCRIBE, EXECUTE, FETCH o PREPARE; ni las directivas INCLUDE o WHENEVER. • No se puede incluir indicadores de parámetros ni referencias a las variables host dentro de la sentencia ejecutada inmediatamente. Ejemplo: En un programa C queremos ejecutar inmediatamente una sentencia que crea una tabla. EXEC SQL EXECUTE INMEDIATE :CREA−TABLA; Otra forma de ejecutar una sentencia es EXECUTE, que ejecuta el nombre−sentencia previamente definido en la sentencia PREPARE (que veremos más adelante). Su sintaxis es la siguiente: EXECUTE nombre−sentencia 2
[USING {:variable−host},... | USING DESCRIPTOR :variable−host] Al igual que su predecesora, también tiene algunas condiciones y restricciones: • Tiene que ir seguida del delimitador de sentencia apropiado. • No puede ejecutar una sentencia SELECT. • Si la sentencia preparada contiene indicadores de parámetros, deben incluir una cláusula USING para sustituir los valores del indicador antes de ejecutar la sentencia. Normalmente se suele usar como indicador el signo de interrogación (?). • Utilice la forma USING :variable−host para permitir la sustitución de parámetros con variables a las que se les asigna valores directamente dentro del programa. Se usará la forma USING DESCRIPTOR como puntero a los descriptores de parámetros. El número de variables proporcionadas o descritas debe ser el mismo que el número de indicadores de parámetros de la sentencia preparada. Vemos que la diferencia de éste último con el anterior es que el EXECUTE necesita de la sentencia PREPARE, que se encarga de compilar y preparar dinámicamente para ejecución la sentencia SQL tal y como esté definida por la cadena de caracteres almacenada en al variable−host. Su sintaxis es la siguiente: PREPARE nombre−sentencia FROM :variable−host Como restricciones tiene las siguientes: • Se puede preparar dinámicamente cualquier sentencia SQL, excepto: CLOSE, DECLARE CURSOR, DESCRIBE, EXECUTE, EXECUTE INMEDIATE, FETCH, OPEN PREPARE o SELECT...INTO, además de que tampoco prepara las directivas INCLUDE y WHENEVER. • No se pueden incluir referencias a variables del lenguaje que soporte SQL en la sentencia SQL preparada. • Se pueden incluir indicadores de parámetros en la variable−host para señalar valores que serán sustituidos cuando se haga referencia a la sentencia preparada dentro de una sentencia OPEN o EXECUTE. Además, se podrán usar indicadores de parámetros siempre que en una sentencia SQL se permitan literales numéricos, cadenas de literales o una variable del lenguaje que soporte SQL, pero no se pueden usar como indicadores tablas, vistas o nombres de columnas. • Se puede referenciar cualquier sentencia preparada dentro de una sentencia DESCRIBE. Solo se puede referenciar una sentencia SELECT preparada dentro de un DECLARE CURSOR dinámica, pero no se puede cuando se ha preparado dentro de una sentencia EXECUTE. • El sistema destruye cualquier definición de sentencia preparada cuando el programa termina o se ejecuta COMMIT WORK. Una vez dados los elementos básicos de inicio en SQL dinámico para ejecución de sentencias, veamos los elementos de programación. Elementos de programación: Delimitadores: Los delimitadores son aquellos elementos que el SQL usa para indicar el final de una sentencia SQL inmersa en un programa de aplicación. Según en qué lenguaje estemos aplicando dicha sentencia, habrá distintos delimitadores. Algunos ejemplos son los siguientes: • COBOL: usa como delimitador END−EXEC[.] al final de cada sentencia SQL (en la misma línea o en una nueva). El punto es opcional en algunas versiones y obligatorio en otras.
3
Ejemplo: sentencia inmersa en COBOL que borra las filas de una tabla llamada ALUMNO. EXEC SQL DELETE FROM ALUMNO END−EXEC[.] • C, Ada o PL/1: usa un punto y coma (;) al final de cada sentencia. Ejemplo: mismo ejemplo anterior, pero en C, Ada o PL/1. EXEC SQL DELETE FROM ALUMNO; • Pascal: usamos el punto y coma (;) como delimitador de proposiciones SQL consecutivas. • Fortran: no necesita delimitadores. Área de comunicaciones (SQLCA = SQL Communications Area): El Área de Comunicaciones del SQL o el SQLCA es una estructura de datos que contiene variables de error e indicadores de estado. Gracias a ella el programa de aplicación puede determinar el éxito o fallo de las sentencias SQL incorporado y actuar correspondientemente. Su parte más importante es la variables SQLCODE, indicadora de errores, y cuyos posibles valores son: • Cero: indica terminación con éxito de la sentencia. • Negativo: indica error serio que impidió la ejecución de la sentencia. • Positivo: indica condición de aviso, por ejemplo, truncamiento o redondeo. NOTA: tanto los valores positivos como los negativos de esta variable son distintos según el error. Variables huéspedes: También llamadas variables principales , pueden utilizarse para almacenar el resultado de una consulta que devuelva una sola fila o bien pueden utilizarse en la propia instrucción SQL. Su sintaxis es: SELECT...INTO :variable Ejemplo: EXEC SQL SELECT nombre INTO :nom_cond FROM conductores WHERE p# = :variable_principal; Las variables que se usen en las instrucciones EXEC SQL, deben aparecer en una sección de declaración en el programa principal (la anteriormente descrita sección BEGIN DECLARE − END DECLARE. Errores WHENEVER: La sentencia WHENEVER es una directiva para el precompilador SQL (y no una sentencia ejecutable) que hace que éste genere automáticamente código para gestionar los posibles errores que se puedan dar a continuación de cada sentencia ejecutable SQL incorporado, y especifica lo que debe hacer este código 4
generado. Se puede usar WHENEVER para informar sobre cómo actuar en tres condiciones de excepción diferentes: • WHENEVER SQLERROR: dice al precompilador que genere código para manejar errores (SQLCODE negativo). • WHENEVER SQLWARNING: lo mismo pero para avisos (SQLCODE positivo). • WHENEVER NOT FOUND: maneja avisos particulares, por ejemplo, los avisos generados por el DBMS cuando el programa trata de recuperar resultados de consultas cuando ya no hay más. Éste es un uso particular del WHENEVER en sentencias SELECT singulares y de la sentencia FETCH (descrita posteriormente). Consultas sin cursores: Las consultas que no requieren cursores son aquellas en las que el resultado de la consulta es una única fila, es decir, las que cuyo resultado puede ser guardado en una variable huésped o principal. Estas consultas serán las que se hagan a través de las sentencias SELECT, DELETE, INSERT o UPDATE. Ejemplo: Queremos cambiar el nombre de un alumno cuyo DNI es 1111 y ponerlo al valor PEDRO. EXEC SQL UPDATE ALUMNO SET A = `PEDRO' WHERE DNI = 1111; NOTA: para las demás sentencias se actuará de forma similar. Consultas con cursores: Las consultas que requieren cursores son en las que el resultado de una consulta devuelve más de una fila. Esta sección la ilustraremos describiendo aquellas sentencias más importantes. DECLARE CURSOR: declara un cursor o puntero a tabla lógica para ser procesado en un programa de aplicación. Su sintaxis es la siguiente: DECLARE nombre_cursor CURSOR OF Sentencia_cursor [{UNION | UNION ALL} sentencia_select]... [[FOR UPDATE OF {nombre_columna},...] | [ORDER BY {{nombre_columna | entero} [ASC | DESC]},...]] La sentencia_select , junto con la opcional UNION con una o más sentencias_select, define la tabla lógica procesada por el cursor. Al igual que todas las sentencias del SQL inmerso, tiene ciertas condiciones y restricciones: • Para actualizar la tabla lógica asociada a un cursor se debe incluir la cláusula FOR DATE, que especifica las columnas elegidas para ser actualizadas. 5
• Para especificar un orden de presentación de las filas de la tabla referenciada se debe incluir una cláusula ORDER BY indicando las columnas cuyos valores indican el orden. Se puede usar, para identificar las columnas, su nombre o su número lógico de posición (el 1 será la primera fila y así sucesivamente). • Si hay nombres de columnas ambiguos(iguales en varias tablas distintas) se usará ORDER BY con el número relativo de cada una de las columnas. • Según la implementación se pueden usar varias columnas ordenadas distintamente. Si no se les especifica orden, ascendente (ASC) será el predeterminado. • No se pueden actualizar aquellas tablas asociadas a cursores cuya declaración contenga una cláusula ORDER BY, una operación UNION ni una sentencia SELECT que incluya una función sobre una columna (AVG, MAX o MIN), la palabra DISTINCT, las cláusulas GROUP BY o HAVING , o una subconsulta que referencie a la misma tabla. Así mismo, tampoco será legítimo si la declaración de la tabla lógica asociada contiene un SELECT que referencie a más de una tabla o a una vista de sólo lectura en la cláusula FROM. En el caso del SQL dinámico la sintaxis es: DECLARE nombre−cursor CURSOR FOR nombre−sentencia En este caso, la diferencia es que el cursor es dinámico. El nombre−sentencia hace referencia al nombre definido en la preparación previa de la sentencia SELECT, que define la tabla lógica. Sus restricciones en cuanto a la cláusula SELECT son iguales, pero además tiene las siguientes: • Pueden incluirse indicadores de parámetros en la sentencia SELECT preparada. Los valores de los indicadores de parámetros serán asignados en la sentencia OPEN. • Para actualizar la tabla lógica asociada a un cursor debe haberse incluido una cláusula FOR UPDATE OF en la sentencia SELECT indicada por nombre−sentencia. • No puede actualizarse una tabla asociada a un cursor cuya definición contenga una cláusula ORDER BY ni UNION. OPEN: abre un cursor declarado anteriormente, de forma que la DBMS comienza a ejecutar una consulta (crea la tabla lógica) y posiciona el cursor asociado justo delante de la primera fila de resultados de la consulta. Cuando se completa con éxito, el cursor está en un estado abierto y listo para ser usado en una sentencia FETCH. Su sintaxis es: OPEN nombre−cursor Para que se pueda usar este cursor sin problemas, se deben cumplir las siguientes condiciones: • El cursor debe estar cerrado cuando el programa ejecuta esta sentencia. Se puede cerrar un cursor usando una sentencia CLOSE o COMMIT WORK. • Para actualizar una tabla cuyo cursor está abierto, se debe incluir en la sentencia DECLARE CURSOR una cláusula FOR UPDATE que especifique las columnas elegidas para la actualización. • Se deberán incluir en esta cláusula otra ORDER BY especificando los nombres (o posiciones lógicas) de las columnas si queremos ordenar la salida. Si no lo especificamos, la secuencia de salida queda indefinida. • No podrán actualizarse las tablas con características similares a las nombradas en la sentencia DECLARE CURSOR. En SQL dinámico, la sintaxis del OPEN es: OPEN nombre−cursor 6
[USING {:variable−host},... | USING DESCRIPTOR :variable−host] Su función es prácticamente similar, con la excepción de que el cursor debe haber sido declarado y preparado anteriormente. Además, si se incluyen indicadores la sentencia SELECT, se deben proporcionar valores de sustitución a través de una lista de variables del lenguaje o de una variable que contenga una estructura SQLDA e la que se describan las variables de sustitución. NOTA: SQLDA = Área de datos, estructura de datos de tamaño variable con dos partes diferenciadas: • Parte fija: al principio del SQLDA. Sus campos identifican la estructura como un SQLDA y especifican el tamaño particular de ésta. • Parte variable: array de una o más estructuras de datos SQLVAR (sus campos describen los datos que se transfieren a la sentencia EXECUTE como valor de parámetro). Cuando se usa un SQLDA para transferir parámetros a una sentencia EXECUTE, debe haber una estructura SQLVAR por cada parámetro. Las restricciones del OPEN dinámico son las mismas que las del anterior. CLOSE: cierra un cursor abierto (un puntero a una tabla lógica), esto es, finaliza el acceso a los resultados de la consulta. Si el cursor no ha sido abierto, se produce un error al intentar cerrarlo. Su sintaxis es la siguiente: CLOSE nombre−cursor NOTA: Cuando se cierra un cursor, la tabla lógica creada al ser abierto es destruida. FETCH: asigna a la lista de variables del lenguaje que soporta al SQL los valores de la fila siguiente del cursor declarado. Cuando se ejecute, el cursor debe estar abierto. Su sintaxis es: FETCH nombre−cursor INTO {:variable−host},... Al igual que en los casos anteriores, tiene restricciones, y son las siguientes: • La sentencia de filas devueltas, a menos que se halla usado un ORDER BY en la declaración del cursor, queda indefinida (en la mayoría de implementaciones). • Los datos soportados por el lenguaje deben ser compatibles con los de las variables devueltas. • Al especificar un número distinto de variables de las definidas, el programa devolverá un aviso.. • Al llegar al final de la tabla lógica referenciada por el cursor, las variables del lenguaje no varían. • Si se produce un error que lleve a valores no válidos o que haga que la posición del cursor sea impredecible, el cursor se cierra automáticamente. En el caso del FETCH usado de forma dinámica (en SQL dinámico) su sintaxis se transforma en: FETCH nombre−cursor USING DESCRIPTOR :variable−host y su misión es la de avanzar el cursor a la siguiente fila disponible de resultados, además de que recupera los valores de sus columnas en las áreas de datos del programa. Al igual que en el caso anterior, el cursor debe estar abierto antes de usar esta sentencia; al igual que al usar esta forma del FETCH se habrán recuperado dinámicamente, mediante una sentencia DESCRIBE, las variables descritas desde el sistema SQL (la lista de variables principales es reemplazada por un SQLDA). Así mismo, la
7
variable−host debe apuntar a un área SQLDA que ha sido previamente inicializada y cuya forma sea aceptada por el sistema. Habrá que tener aquí también las condiciones que se establecían para el FETCH anterior. Bibliografía: • Aplique SQL , James R. Groff & Paul N. Weinberg, Ed. OSBORNE McGraw Hill, 1991 • SQL, Guía de referencia rápida, John Viescas, Ed. Anaya, 1990 • Documentos varios procedentes de Internet.
8