Manipulación de datos 6.1 Introducción Ahora abordaremos los aspectos de manipulación de datos de SQL. Este lenguaje ofrece cuatro proposiciones en DML – SELECT (seleccionar), UPDATE (actualizar), DELETE ( borrar) e INSERT (insertar)- el presente capitulo describe las caracteristicas principales de los cuatro. Como se explico en la sección 4.3 las tablas manipuladas con esas proposiciones de DML pueden ser, en términos generales, tanto tablas base como vistas. Pero este capitulo nos ocuparemos de manera exclusiva de las tablas base. Las vistas se analizan en el capítulo 8. Este capitulo esta organizado como sigue: las secciones 6.2 a 6.5 se ocupan de las operaciones de recuperación de datos y la sección 6.6 estudia las operaciones de actualización ( término en el cual incluimos, como ya es costumbre, todas las operaciones con UPDATE, DELETE e INSERT) en términos más específicos: La sección 6.2 presenta las características principales de la proposición select básica La sección 6.3 explica como puede utilizarse select para expresar diversos tipos de reunión ( en particular, la llamada reunión natural). La sección 6.4 analiza las funciones de agregados COUNT (cuenta), SUM (suma) AVG (promedio), etcétera; en particular describe el empleo de las cláusulas GROUP BY (agrupar por) y HAVING (con) junto con esas funciones. La sección 6.5 describe varias características adicionales: LIKE (como), la condición IS NULL (es nulo), subconsultas, EXISTS (existe) y UNION (unión). Estas construcciones, aunque bastante importante en algunos casos, suelen utilizarse con menor frecuencia en la práctica que las que se describen en las secciones 6.2 a 6.4 razón por la cual se revisa en una sección aparte. La sección 6.6 esta dedicada a las proposiciones UPDATE (actualizar), DELETE (eleiminar) e INSERT (insertar). Por ultimo trataremos de vincular varias de las ideas presentadas en el cuerpo del capitulo. Para ello la sección 6.7 presentará como ejemplo una consulta muy compleja y mostrará en principio cómo podría ejecutarse. No es nuestra intención ofrecer un tratamiento complejo del lenguaje de manipulación de datos (DML) de SQL; sólo queremos ilustrar sus características principales. Con todo, el capitulo es bastante largo, y el lector quizá prefiera abordarlo sección por sección. La sección 6.5 puede omitirse en una primera lectura, si se desea. Nota: muchos de los ejemplos siguientes sobre todo los de la sección 6.5 son bastante complejos. El lector deberá sacar la conclusión de que lo complejo radica en el lenguaje SQL mismo, con ello queremos subrayar más bien el hecho de que las operaciones comunes son tan sencillas en SQL (y, de hecho, en casi todos los lenguajes relacionales) que los ejemplos de tales operaciones resultan poco interesantes, y no iluistran toda la potencia del lenguaje. Por supuesto, también mostraremos algunos ejemplos sencillos. Como siempre, los ejemplos utilizan la base de datos de proveedores y partes. 6.2 CONSULTAS SIMPLES Comenzaremos con un ejemplo sencillo: la consulta debe “obtener el número y la situación de todos los proveedores de París “ la cual puede expresarse en SQL así: SELECT S#, SITUACION FROM S WHERE CIUDAD = “ París”; -----------------Resultado: S# Situación ---------------S2 10 S3 30 Este ejemplo ilustra la forma más común de la proposición SELECT en SQL: “SELECT (seleccionar) los campos especificados FROM (de) la tabla especificada WHERE (donde) se cumpla la condición especificada”. El primer punto por destacar es que el resultado de la consulta es otra tabla, derivada en alguna forma de las tablas existentes en la base de datos. Dicho de otro modo, el usuario de un sistema relacional trabaja siempre dentro del marco de referencia tabular sencillo, lo cual es una característica muy atractiva de esos sistemas. (por esta razón decidimos que las tablas relacionales constituyen un sistema cerrado bajo los operadores de recuperación de datos de un lenguaje como SQL. Hablaremos muchos más de cerca de la cerradura en la parte III del presente libro.) Por cierto podríamos haber formulado la consulta empleando nombres de campo calificados en todos los casos: SELECT S.S# , S.SITUACIÓN FROM S WHERE S.CIUDAD = “París“ Un nombre de campo calificado se compone de un nombre de tabla ( o nombre de variable de recorrido) y un nombre de campo, en ese orden, separados mediante un punto. Nunca es indebido utilizar nombres calificados, y en algunos casos es indispensable, como se verá en la sección 6.3. Como referencia, mostramos enseguida la forma general de la proposición SELECT ( pasando por alto la posibilidad de UNION, la cual se explicara más delante de este capitulo): SELECT [DISTINCT] elemento (s) FROM tabla (s) [WHERE condición] [GROUP BY campo(s)] [HAVING condición] [ ORDER BY campo (s)]; Enseguida mostraremos las características principales de esta proposición mediante una seria bastante larga de ejemplos. 6.2.1 RECUPERACIÓN SIMPLE Obtener los números de parte de todas las partes suministradas . SELECT P# FROM tabla (s) RESULTADO: P# P1 P2 P3 P4 P5 P6 P1 P2 P2 P2 P4 P5 Obsérvese la repetición de números de parte en este resultado. SQL no elimina filas repetidas del resultado de una proposición SELECT si el usuario no se lo pide de manera explícita mediante la palabra DISTINT (distinto), como en: SELECT DISTINT P# FROM SP; RESULTADO: P# P1 P2 P3 P4 P5 P6 Nota: en este ejemplo especifico sucede que cada fila contiene un nuevo valor escalar; por tanto, el efecto de DISTINT en este caso es eliminar valores escalares repetidos. Pero nótese, que en general el significado de DISTINT es “eliminar filas repetidas”. Véase el ejemplo 6.3.5 en la siguiente sección como ilustración. 6.2.2 Recuperación de valores calculados. Obtener, para todas las partes, el número de parte y su peso en gramos ( los pesos de partes se dan en libras en la tabla P). SELECT P.P#, ‘peso en gramos =’, P. PESO * 454 FROM P ----- -----------------------------Resultado P# ---- -----------------------------P1 peso en gramos = 5448 P2 peso en gramos = 7718 P3 peso en gramos = 7718 P4 peso en gramos = 6356 P5 peso en gramos = 5448 P6 peso en gramos = 8626 La cláusula SELECT ( y también las cláusulas WHERE y HAVING, véanse los ejemplos correspondientes) puede incluir expresiones escalares generales, empleando por ejemplo, operadores escalares con más y menos y funciones escalares como SUBSTR (subcadena), en vez de nombres simples de campo, o además de ellos. 6.2.3 Recuperación simple (“ SELECT*”). Obtener datos completos de todos los proveedores. SELECT * FROM S Resultado una copia de toda la tabla S. El asterisco representa una lista de todos los nombres de campo de la tabla o tablas nombradas en la cláusula FROM (de), en el orden de definición de esos campos en las proposiciones CREATE ( y quizá ALTER) TABLE pertinentes. Así la proposición SELECT mostrada equivale a: SELECT S.S# FROM S; La notación del asterisco es cómoda para consultas interactivas, ya sea que ahorra tecleo. No obstante, puede ser peligrosa en SQL embebido ( o sea, SQL dentro de un programa de aplicación) porque el significado de “*” puede cambiar si se religa el programa y entre tanto se ha modificado de alguna forma la definición de la tabla (sobre todo si se ha añadido otra columna). Este libro utilizara “SELECT *” solo en contextos donde no pueda provocar problemas ( casi en todos los contextos interactivos), y recomendamos a los usuarios hacer lo mismo en la práctica. 6.2.4. recuperación calificada. Obtener los números de los proveedores radicados en París cuya situación sea mayor de que 20. SELECT S# FROM S WHERE CIUDAD = París AND SITUACIÓN > 20: ------Resultado S# -----S3 La condición que sigue a WHERE ( donde) puede incluir los operadores de comparación = ,<> (diferente de), >, >=, <, <=; los operadores booleanos AND, OR y NOT; y paréntesis para indicar un orden de evaluación deseado. 6.2.5 recuperación por ordenamiento Obtener números de proveedor y situación de los proveedores en París, en orden descendente por situación. SELECT S#, SITUACION FROM S WHERE CIUDAD= ‘París’ ORDER BY SITUACION DESC; Resultado: S# SITUACIÓN ------------------S3 30 S2 10 En general no se garantiza un orden particular en la tabla de resultado, pero aquí el usuario especificó la organización del resultado en un cierto orden antes de su presentación. El ordenamiento puede especificarse igual que en CREATE INDEX (crear índice) como se observa en la sección 5.3, es decir, Columna [orden ] [, columna [orden] ] Donde, como el caso de CREATE INDEX, “orden” puede ser asc o desc, y asc es el orden por omisión También es posible identificar columnas en la cláusula ORDER BY (ordenar por) empleando números de columna en vez de nombres; es decir, según la posición ordinal (izquierda a derecha) de la columna en cuestión dentro de la tabla de resultado. Esta característica hace posible utilizar como criterio de ordenamiento del resultado una columna sin nombre. Por ejemplo, si queremos organizar el resultado del ejemplo 6.2.2 en orden ascendente por número de parte dentro de un orden ascendente por peso en gramos: SELECT P.P#, ‘ peso en gramos =’, P:PESO * 454 FROM P ORDER BY 3, P# ; /* o bien “ ORDER BY 3, 1;”*/ El 3 se refiere a la tercera columna de la tabla de resultado, Resultado: P -------P1 P5 P4 P2 P3 P6 6.3 -----------------------------------------peso en gramos = 5448 peso en gramos = 5448 peso en gramos = 6356 peso en gramos = 7718 peso en gramos = 7718 peso en gramos = 8626 CONSULTAS DE REUNIÓN La posibilidad de “reunir” dos o más tablas en una sola es una de las características más poderosas de los sistemas relacionales. De hecho, la disponibilidad de la operación de reunión es, casi más importante que cualquier otra cosa, lo que distingue a los sistemas relacionales de los no relacionales. Así pues, ¿ qué es una reunión? en términos informales, es una consulta en la cual se obtiene información de más de una tabla de una tabla. He aquí un ejemplo sencillo. 6.3.1 Equirreunión simple. Obtener todas las combinaciones de información de proveedores y partes tales que el proveedor y la parte en cuestión estén situados en la misma ciudad ( es decir, estén “consituados”; valga intentar un término antiestético pero cómodo). SELECT S. *, P.* FROM S, P WHERE S.CIUDAD = P. CIUDAD; Adviértase que aquí las referencias a campos en la cláusula WHERE deben calificarse con los nombres de las tablas correspondientes (pues de lo contrario serían ambiguas) Resultado. S# SNOMBRE SITUACIÓN S.CIUDAD P.NOMBRE COLOR PESO P.CIUDAD S1 S1 S1 S2 S2 S2 S2 S4 S4 S4 Salazar Salazar Salazar Jaimes Jaimes Bernal Bernal Corona Corona Corona 20 20 20 10 10 30 30 20 20 20 Londres Londres Londres París París París París Londres Londres Londres P1 Tuerca P4 Birlo P6 Tuerca P2 Perno P5 Leva P2 Perno P5 Leva P1 Tuerca P1 Birlo P6 Engrane Rojo Rojo Rojo Verde Azul Verde Azul Rojo Rojo Rojo 12 Londres 14 Londres 19 Londres 17 París 12 París 17 París 12 París 12 Londres 14 Londres 19 Londres En este resultado hemos mostrado de manera explícita las dos columnas CIUDAD como P.CIUDAD y S.CIUDAD, por claridad. Explicación: por el enunciado del problema en español es evidente que los datos requeridos provienen de dos tablas, a saber, S y P. Por tanto, en la formulación en SQL de la consulta, nombramos primero esas dos tablas en la cláusula FROM (de) y en seguida expresamos la conexión entre ellas ( o sea, el hecho de que los valores CIUDAD deben ser iguales) en la cláusula WHERE (donde). Para entender el funcionamiento de esa proposición, considérense cualesquiera dos filas, una de cada tabla; por ejemplo estas dos: S# SNOMBRE SITUACIÓN S.CIUDAD P.NOMBRE COLOR PESO P.CIUDAD S1 Salazar 20 Londres P4 Birlo Rojo 14 Londres idénticas Estas dos filas demuestran que el proveedor S1 y la parte de P1 si están “cosituadas” (pues el valor de ciudad es el mismo en los dos casos). Por lo tanto, generan la fila de resultado --- ------------ ------------ ---------- ----- ----------- ------- ----- -------------S# S. nombre situación S. ciudad P# P nombre color peso P. ciudad S1 Salazar 20 Londres P1 Tuerca Rojo 12 Lóndres -------------------------------------------------------------------------------------------------(ya que la satisfacen la condición de la cláusula WHERE; a saber, S.CIUDAD = P.CIUDAD). Lo mismo sucede con las demás parejas de filas en las cuales los valores CIUDAD concuerdan. Nótese que el proveedor S5 (situado en Atenas) no aparece en el resultado, por que no hay partes almacenadas en Atenas; de manera similar, la parte P3 (almacenada en Roma) tampoco aparece en el resultado, por que no hay proveedores situados en Roma. Se dice que el resultado de esta reunión de las tablas S y P con base en valores de CIUDAD iguales. También se usa el termino de “reunión” para designar la operación de construir el resultado de este tipo. Se dice que la condición S.CIUDAD = P.CIUDAD es una condición de reunión. Dicho sea de paso, no es obligatorio emplear la igualdad como operador de la comparación en una condición de reunión, aunque es lo mas frecuente. El ejemplo 6.3.2 ilustra una reunión donde se emplea el operador mayor que. Pero si el operador es la igualdad, la reunión recibe a veces la designación explícita de “equirreunión”. Por definición la equirreunión debe producir un resultado de dos columnas idénticas (véase el ejemplo anterior). Si se elimina una de esas dos columnas, quedará lo que se conoce como reunión natural. He aquí un ejemplo de reunión natural( la de S y P con base en las ciudades): SELECT S#, SNOMBRE, SITUACIÓN, S.CIUDAD P#, PNOMBRE, COLOR, PESO FROM S, P WHERE S.CIUDAD = P.CIUDAD; La reunión natural es definitivamente el tipo de reunión más útil; tanto así que el termino reunión sin calificativo casi siempre se emplea para referirse de manera especifica a la reunión natural. Enseguida se presenta una forma distinta ( y útil) de enfocar la construcción conceptual de las reuniones. Primero, se forma el producto se forma el producto cartesiano de las tablas incluidas en la cláusula FROM (de). El producto cartesiano de un conjunto de n tablas es la tabla formada por las filas f posibles, donde f es la concatenación de una fila de la primera tabla, una fila de la segunda tabla,…, y una fila de la enésima tabla, por ejemplo, el producto cartesiano de la tabla s y la tabla p (en ese orden) es la siguiente tabla: S# S1 S1 S1 S1 S1 S1 S2 . . . S5 S nombre Salazar Salazar Salazar Salazar Salazar Salazar Jaimes Situación 20 20 20 20 20 20 10 Ciudad Londres Londres Londres Londres Londres Londres París P# P1 P2 P3 P4 P5 P6 P1 P nombre Tuerca Perno Birlo Birlo Leva Engrane Tuerca Color rojo verde Azul rojo Azul rojo rojo Aldana 30 Atenas P6 Engrane rojo Peso 12 17 17 14 12 19 12 Ciudad Londres París Roma Londres París Londres Londres 19 Londres La tabla contiene 5*6 =30 filas. Elimínense ahora de este producto cartesiano todas las filas que no satisfagan la condición de reunión. Lo restante es la reunión requerida. En el presente caso, eliminamos todas las filas en las cuales S. CIUDAD no es igual a P. CIUDAD y lo que queda exactamente es la equirreunión antes mostrada. 6.3.2 Reunión mayor que Obtener todas las combinaciones de información de proveedor y parte donde la ciudad del proveedor siga a la ciudad de la parte en el orden alfabetico. SELECT S.*, P.* FROM S, P WHERE S.CIUDAD > P.CIUDAD; Resultado S# S2 S2 S2 S3 S3 S3 S nombre Jaimes Jaimes Jaimes Bernal Bernal Bernal Situación 10 10 10 30 30 30 Ciudad París París París París París París P# P1 P4 P6 P1 P4 P6 P nombre Tuerca Birlo Engrane Tuerca Birlo Engrane Color rojo rojo rojo rojo rojo rojo Peso 12 14 19 12 14 19 Ciudad Londres Londres Londres Londres Londres Londres 6.3.3 Consulta de reunión con una condición adicional. Obtener todas las combinaciones de información del proveedor y parte donde el proveedor y la parte en cuestión estén cosituados, pero omitiendo a los proveedores cuya situación sea 20 SELECT S.*, P.P# FROM S, P WHERE S.CIUDAD = P.CIUDAD AND S.SITUACIÓN <> 20; Resultado: S# S2 S2 S3 S3 S nombre Jaimes Jaimes Bernal Bernal Situación 10 10 30 30 Ciudad París París París París P# P2 P5 P2 P5 P nombre Perno Leva Perno Leva Color Verde Azul Verde azul Peso 17 12 17 12 Ciudad París París París París La cláusula WHERE en un SELECT de una reunión puede incluir otras condiciones además de la condición de la reunión. 6.3.4 Recuperación de campos específicos de una reunión. Obtener todas las combinaciones de un número proveedor/número de parte tales que el proveedor y la parte en cuestión estén cosituados SELECT S.S# , P.P# FROM S, P WHERE S.CIUDAD = P.CIUDAD; Resultado: S# S1 S1 S1 S2 S2 S3 S3 S4 S4 S4 P P1 P4 P6 P2 P5 P2 P5 P1 P4 P6 Desde luego también es posible seleccionar sólo los campos especificados de una reunión, en vez de seleccionarlos todos por fuerza. 6.3.5 Reunión de tres tablas. Obtener todas las parejas de nombres de ciudad tales que un proveedor situado en la primera ciudad suministre una parte almacenada en la segunda ciudad. Por ejemplo, el proveedor S1 suministra la parte P1; el proveedor S1 está situado en Londres, y la parte P1 se almacena en Londres; por lo tanto, (Londres, Londres) es una pareja de ciudades en el resultado. SELECT DISTINCT S.CIUDAD, P.CIUDAD FROM S, SP, P WHERE S.S# = SP.S# AND SP.P# = P.P#; Resultado S. CIUDAD P.CIUDAD Londres Londres Londres París Londres Roma París Londres París París No hay un limite intrínseco para el número de tablas que se pueden juntar. Adviértase que se uso DISTINCT (distinto) en el ejemplo con el objeto de eliminar las parejas repetidas. 6.3.6 Reunión de una tabla consigo misma Obtener todas las parejas de números de proveedor tales que dos proveedores en cuestión estén cosituados. SELECT PRIMERA.S#, SEGUNDA.S# FROM S.PRIMERA, S.SEGUNDA WHERE PRIMERA.CIUDAD = SEGUNDA.CIUDAD AND PRIMERA.S# < SEGUNDA.S# Resultado: S# S1 S2 S S4 S3 Esta consulta implica una reunión de la tabla S consigo misma ( con base en igualdad de ciudades) según se explica enseguida. Supongamos por un momento que tenemos dos copias separadas de la tabla S, la “primera” y la “segunda”. Entonces, la lógica de la consulta será la siguiente: necesitamos poder examinar todas las parejas posibles de filas de proveedores, una de la primera copia de S y la otra de la segunda, y extraer los dos números de proveedor de una de esas parejas de filas y sólo si los valores de ciudad son iguales. Por tanto, necesitamos poder hacer referencia a dos filas de proveedor al mismo tiempo. Para distinguir entre las dos referencias, introducimos las variables de recorrido arbitrarias primera y segunda, cada una de las cuales recorre la tabla S. En todo momento PRIMERA representa alguna fila de la primera copia de, y SEGUNDA representa alguna fila de la segunda copia. El resultado de la consulta se obtiene examinando todas la posibles parejas de valores PRIMERA/SEGUNDA y probando la condición WHERE en cada caso. Nota: el propósito de la condición PIMERA.S# < SEGUNDA.S# es doble: a) Elimina las parejas de los números de proveedor de la forma (x,x) b) Garantiza la aparición de sólo una de las parejas (x,y) y (y,x). Éste primer ejemplo que hemos visto en el cual ha sido necesario el empleo de variables de recorrido. Sin embargo, nunca es erróneo introducir tales variables, aun cuando no sea indispensable su empleo, y en ocasiones puede hacer más clara la proposición. 6.4 FUNCIONES AGREGADAS Aunque es bastante poderosa en muchos aspectos la proposición SELECT tal como se ha descrito hasta ahora resulta inadecuada para muchos problemas prácticos. Por ejemplo, aun una consulta tan sencilla como “ ¿ Cuantos proveedores hay? “ No puede expresarse empleando las construcciones utilizadas hasta ahora. Por tanto, SQL ofrece una serie de funciones agregadas especiales para ampliar su capacidad básica de recuperación de información. Estas funciones son COUNT(cuenta), SUM (suma), AVG (promedio), MAX (máximo), MIN (mínimo). Sin tomar en cuenta el caso especial de “COUNT (*)” (véase lo siguiente), cada una de las funciones trabaja sobre el total de valores en una columna de alguna tabla, quizá una tabla derivada, es decir, una tabla construida como resultado de alguna consulta y produce un solo valor como resultado según estas definiciones: COUNT - número de valores en la columna SUM - suma de los valores de la columna AVG - promedio de valores de la columna MAX - valor más grande de la columna MIN - valor más pequeño de la columna En el caso de SUM y AVG la columna debe contener valores numéricos. En general, el argumento de la función puede ir precedido de manera opcional por la palabra clave DISTINC para indicar que los valores repetidos deben eliminarse antes de que se aplique la función. En el caso de MAX y MIN, empero, DISTINCT sale sobrando y no tiene efecto alguno. En el caso de COUNT, es necesario especificar DISTINCT; se incluye la función especial COUNT (*), - no se permite DISTINCT – para contar todas las filas de una tabla sin eliminación de duplicados. Si hay nulos en la columna del argumento, se eliminarán antes de aplicarse la función, se especifique o no la palabra clave DISTINCT (excepto en el caso de COUNT (*), donde los valores nulos se manejan igual que los no nulos). Si el argumento es un conjunto vacío, COUNT devuelve el valor de cero; las demás funciones devuelven el valor nulo. 6.4.1 Función de agregados en la Cláusula SELECT Obtener el número total de proveedores. SELECT COUNT (*) FROM S Resultado 5 Obsérvese que el resultado en una tabla, con una fila y una columna (sin nombre) y contiene un solo valor escalar 5. 6.4.2 Función de agregados en la Cláusula SELECT, con DISTINCT Obtener el número total de proveedores que suministran partes en la actualidad. SELECT COUNT (DISTINCT S#) FROM SP; Resultado: 4 6.4.3 función de agregados en la cláusula SELECT, con una condición obtener el número de envíos de la parte P2 SELECT COUNT (*) FROM SP WHERE P# = ‘P2’ ; Resultado 4 6.4.4 función de agregados en la cláusula SELECT, con una condición obtener la cantidad suministrada de la parte P2 SELECT SUM (CANT) FROM SP WHERE P# = ‘P2’; Resultado ------1000 6.4.5 Empleo de GROUP BY (agrupar por) El ejemplo 6.4.4 mostró cómo puede calcularse la cantidad total suministrada de una parte especifica. Pero vamos a suponer que deseamos calcular la cantidad total suministrada de cada parte: es decir, para cada parte suministrada, obtener el número de parte y la cantidad total enviada de esa parte. SELECT P# , SUM (CANT) FROM SP GROUP BY P#; Resultado: P# P1 P2 P3 P4 P5 P6 -----600 1000 400 500 500 100 Explicación: el operador GROUP BY reorganiza en el sentido lógico la tabla representada por la cláusula FROM formando particiones o grupos, de manera que dentro de un grupo dado todas las filas tengan el mismo valor en el campo de GROUP BY. (Desde luego, tal agrupamiento es meramente conceptual; la tabla no se reorganiza en la base de datos) En el ejemplo, la tabla SP se agrupa de manera tal que un grupo contiene todas las filas de la parte P1, otro contiene todas las filas de la parte P2, etcétera. En seguida se aplica la cláusula SELECT a cada grupo de la tabla dividida ( y no a cada fila de la tabla original) cada expresión en la cláusula SELECT debe producir un solo valor por grupo; es decir, puede ser el campo de GROUP BY mismo ( o quizá alguna expresión aritmética que implique a ese campo), o un literal, o una función como SUM, la cual opera sobre todos los valores de un campo dado entero de un grupo y los reduce a un solo valor. Adviértase que GROUP BY no implica ORDER BY (ordenar por); si queremos presentar el resultado del ejemplo anterior ordenado por P# , deberemos especificar también la cláusula ORDER BY P# ( después de la cláusula GROUP BY). Una tabla se puede agrupar según cualquier combinación de sus campos. En la sección 6.7 se presenta un ejemplo de la agrupación según más de un campo. 6.4.6 Empleo de HAVING (con) Obtener los números de todas las partes suministradas por más de un proveedor. SELECT P# FROM SP GROUP BY P# HAVING COUNT(*) > 1; Resultado P# P1 P2 P4 P5 HAVING es a los grupos lo que WHERE es a las filas ( si se especifica HAVING, deberá haberse especificado también GROUP BY). En otras palabras HAVING (con) sirve para eliminar grupos de la misma manera como WHERE (donde) sirve para eliminar filas. Las expresiones en una cláusula HAVING deben producir un solo valor por grupo. 6.5 CARACTERISTICAS AVANZADAS En esta sección ilustraremos diversas características de la proposición SELECT. A falta de un término mejor, llamaremos avanzadas a esas características, aunque en realidad no son más complejas que algunos de los conceptos ya analizados en las ultimas tres secciones: de hecho algunas de ellas, - sobre todo EXISTS (véanse los ejemplos 6.5.7 a 6.5.10) – son fundamentales; con todo, quizá se les encuentre con menor frecuencia en la práctica que las características analizadas en las secciones 6.2 a 6.4. por tal motivo, el lector puede optar por dejar a un lado esta sección en una primera lectura. 6.5.1 recuperación de datos con LIKE (como) Obtener todas las partes cuyos nombres comiencen con la letra B. SELECT P.* FROM P WHERE P.NOMBRE LIKE ‘B%’; Resultado: P# P3 P4 PNOMBRE Birlo Birlo COLOR azul rojo PESO CIUDAD 17 Roma 14 Londres En general, una condición LIKE (como) adopta la forma Columna LIKE literal-de-cadena Donde “columna” debe designar una columna de tipo de cadena ( CHAR o VARCHAR o GRAPHIC o VARGRAPHIC). Para un registro dado, la condición se cumple si el valor dentro de la columna designada sigue el patrón especificado con “literal”. Los caracteres dentro de “literal” se interpretan de esta manera: El carácter “_” (subrayado) representa cualquier carácter individual. El carácter “%” ( por ciento) representa cualquier secuencia de n caracteres (donde n puede ser cero) Todos los demás caracteres se representan por si mismos. En el ejemplo, entonces, la proposición SELECT extraerá registros de la tabla P en los cuales el valor de PNOMBRE comience con la letra B seguida de cualquier secuencia de cero o más caracteres. He aquí algunos ejemplos más de LIKE ( y su opuesto, NOT LIKE (diferente de)); DOMICILIO LIKE ‘%LIMA%’- Se cumplirá si domicilio contiene la cadena “LIMA” En cualquier posición. S# LIKE ‘S_’ - Se cumplirá si S# tiene una longitud de tres caracteres Y el primero es una S PNOMBRE LIKE ‘%c___ - Se cumplirá si PNOMBRE tiene una longitud de cuatro o más caracteres y el tercero antes del ultimo es una c. CIUDAD NOT LIKE ‘%E%’ - Se cumplirá si CIUDAD no tiene una E 6.5.2 Recuperación de datos que incluyen NULL (nulo) Vamos a suponer para este ejemplo que la situación del proveedor S5 tiene un valor nulo, en vez de 30. Obtener los números de los proveedores cuya situación es mayor que 25. SELECT S# FROM S WHERE SITUACIÓN >25 Resultado: S# S3 El proveedor S5 no cumple la condición. Como se menciono en la sección 5.2, cuando al evaluar una condición se compara un nulo con cualquier otro valor, sea cual sea el operador de comparación empleado, el resultado de la comparación nunca se considera verdadero, aún cuando el otro valor también sea nulo. En otras palabras si SITUACIÓN tiene un valor nulo, ninguna de las siguientes comparaciones resulta verdadera: SITUACIÓN >25 SITUACIÓN <= 25 SITUACIÓN = 25 SITUACIÓN <> 25 SITUACIÓN = NULL /* Esta sintaxis no es legal. Ver lo que sigue */ SITUACIÓN <> NULL /* Ésta tampoco */ Se incluye una condición especial con la forma Nombre – columna IS [ NOT] NULL Para detectar la presencia o ausencia de nulos. Por ejemplo: SELECT S# FROM S WHERE SITUACIÓN IS NULL Resultado: S# S5 La sintaxis “SITUACION = NULL” no esta permitida, por que nada, ni siquiera un nulo se considera igual a otro nulo. 6.5.3 Recuperación de datos con subconsulta. Obtener los nombres de los proveedores que suministran la parte P2 SELECT SNOMBRE FROM S WHERE S in ( SELECT S# FROM SP WHERE P# = ‘P2’ ); Resultado : SNOMBRE Salazar Jaimes Bernal Corona Explicación: una subconsulta es (en términos informales) una expresión SELECT … FROM… GROUP BY …HAVING anidada dentro de otra expresión del mismo tipo. Las subconsultas se utilizan por lo regular para representar el conjunto de valores en el cual se realizará una búsqueda mediante una “condición IN (en)“, como puede verse en el ejemplo. Para evaluar la consulta completa, el sistema evalúa primero la subconsulta anidada (por lo menos desde el punto de vista conceptual). Esa subconsulta produce como resultado el conjunto de números de proveedor de los proveedores que suministran la parte P2, o sea el conjunto (S1,S2,S3,S4). Así, la consulta original equivale a esta otra consulta mas sencilla: SELECT NOMBRE FROM S WHERE S# IN (‘S1’,’ S2’,’S3’, ‘S4’); La cláusula WHERE esta en forma más sencilla si cumple si y sólo si S# tiene uno de los valores S1,S2,S3,S4; por lo tanto, el resultado final es el que se mostró antes. Adviértase, por cierto, que el problema original -“ obtener los nombres de los proveedores que suministran la parte P2“ – se puede expresar también como una consulta de reunión así: SELECT FROM WHERE AND S,S.NOMBRE S,SP S.S# = SP.S# SP.P# = ‘P2’; Explicación: la reunion y de S y SP con base en números de proveedor se compone de una tabla de doce filas (una por cada fila de SP), en la cual cada fila se compone de la fila correspondiente de SP ampliada con los valores SNOMBRE, SITUACIÓN Y CIUDAD del proveedor identificado por el valor S# de esa fila. de estas doce filas, cuatro corresponden a la parte P2; de modo que el resultado final se obtiene extrayendo los valores SNOMBRE de esas cuatro filas. Las dos formulaciones de la consulta original – una mediante subconsulta y la otra mediante reunión – son igualmente correctas. La elección de una u otra será cuestión sólo del gusto del usuario (excepto que –según lo avanzado del optimizador del sistema – una de las dos podría representar mejor desempeño; en un sistema ideal, desde luego, el usuario no tendría que preocuparse por ese tipo de consideraciones). 6.5.4 subconsulta con varios niveles de anidamiento Obtener los nombres de los proveedores que suministren por lo menos una parte roja. SELECT SNOMBRE FROM S (WHERE S# IN SELECT S# FROM SP (WHERE P# FROM P WHERE COLOR =’ROJO’)); Resultado: SNOMBRE Salazar Jaimes Corona Las subconsultas pueden estar anidadas a cualquier profundidad. Ejercicio: presentar una formulación de reunión equivalente para esta consulta. 6.5.5 Subconsulta con operador de comparación distinto de IN Obtener los números de los proveedores situados en la misma ciudad que el proveedor S1. SELECT S# FROM S WHERE CIUDAD = (SELECT CIUDAD FROM S WHERE S# = ‘S1’); Resultado: S# S1 S4 Si el usuario sabe que el resultado de una subconsulta determinada será exactamente un valor, puede utilizar un operador de comparación escalar sencillo (como 0,>,etcétera) en vez del IN acostumbrado. 6.5.6 función de agregados en una subconsulta obtener los números de los proveedores cuya situación sea menor que el valor máximo actual de situación en la tabla 5. SELECT SNOMBRE FROM S WHERE SITUACION < (SELECT MAX (SITUACION) FROM S); Resultado: S# S1 S2 S4 6.5.7 consulta con EXISTS (existe). Obtener los nombres de los proveedores que suministran la parte P2 ( igual que en el ejemplo 6.5.3) SELECT SNOMBRE FROM S WHERE EXISTS (SELECT FROM SP WHERE S# = S.S# AND P# = ´P2´); Explicación: EXISTS (existe) representa aquí el cuantificador existencial, la expresión “EXISTS ( SELECT … FROM…)” da como resultado un valor verdadero si y sólo si el resultado de evaluar la subconsulta “SELECT …FROM …” no es el conjunto vacío; en otras palabras, si y sólo si existe un registro en la tabla FROM de ese “ SELECT …FROM …”, adviértase cómo la condición WHERE dentro de la expresión EXISTS hace referencia a la tabla FROM del nivel externo de la consulta; lo hace a través de un nombre de columna calificado de esa manera explícita (S.S#, en el ejemplo). Para entender como funciona el ejemplo, considérese en orden cada valor SNOMBRE, y véase si hace que se cumpla la prueba de existencia. Vamos a suponer que el primer valor de SNOMBRE es “Salazar” ( de modo que el valor correspondiente de S# es S1). ¿ Esta vacío el conjunto de los registros SP en los cuales S# es igual a S1 y P# es igual a P2, de manera que “Salazar” deberá ser uno de los valores extraídos. Lo mismo hace con los valores de SNOMBRE. Así pues, otra forma de enunciar la consulta podría ser: “seleccionar nombres de proveedores tales que exista un envío que relacione esos proveedores con la parte P2”. Aunque este ejemplo en particular solo ilustra otra manera de formular una subconsulta para un problema que ya sabemos como manejar (mediante una reunión o bien una subconsulta), la construcción EXISTS es en realidad una de las más básicas y generales de todo el lenguaje de SQL. (Adviértase en particular que cualquier consulta en la cual se utilice IN siempre podrá utilizar a modo EXIST, lo opuesto, en cambio, no se cumple.) la forma negada, NOT EXISTS (no existe) es importante sobre todo en cierto tipo de consultas. 6.5.8 Consulta con NOT EXISTS Obtener los nombres de los proveedores que suministren la parte P2 ( lo inverso al ejemplo anterior). SELECT SNOMBRE FROM S WHERE NOT EXISTS ( SELECT * FROM SP WHERE S# = S.S# AND P# = ‘P2’); Resultado SNOMBRE Aldana Otra forma de enunciar la consulta es: ” Seleccionar nombres de los proveedores tales que no exista un envío que relacione a esos proveedores con la parte P2”. Cabe señalar que esta consulta sería equivalente a otra expresada en términos de la forma negada de IN, a saber: SELECT SNOMBRE FROM S WHERE S# NOT IN (SELECT S FROM SP WHERE P# = P2); 6.5.9 Consulta con NOT EXISTS Obtener los nombres de los proveedores que suministren todas las partes SELECT SNOMBRE FROM S WHERE NOT EXISTS (SELECT * FROM P WHERE NOT EXIST (SELECT * FROM SP WHERE S# = S.S# AND P# = P.P#)); Resultado SNOMBRE Salazar La consulta podría enunciarse también así: “obtener los nombres de los proveedores tales que no exista una parte que no suministren”. Adviértase que esta consulta no podría expresarse usando la forma negada de IN. 6.5.10 Consulta con NOT EXISTS. Obtener los números de los proveedores que suministran por lo menos todas las partes suministradas por el proveedor S2. Una forma de encarar este problema más bien complejo es descomponerlo en una serie de consultas más simples y manejarlas una por una. Así, podemos encontrar primero el conjunto de números de parte de las partes suministradas por el proveedor S2; SELECT P# FROM SP WHERE S# = ‘S2’; Resultado: P# P1 P2 Si utilizamos CREATE TABLE e INSERT ( lo cual analiza en forma detallada la siguiente sección), es posible guardar este resultado en una tabla de la base de datos digamos la tabla TEMP, ahora podemos encontrar el conjunto de números de proveedores que suministran todas las partes incluidas en TEMP. SELECT DISTINCT S# FROM SP SPX WHERE NOT EXISTS (SELECT * FROM TEMP WHERE NOT EXISTS (SELECTS * FROM SP SPY WHERE SPY.S# = SPX.S# AND SPY.P# = TEMP.P#)); Resultado: S# S1 S2 Puede notarse que esta consulta difiere de la del ejemplo 6.5.9 en la necesidad de utilizar por lo menos una variable de recorrido explícita, pues estamos extrayendo valores de S# de la tabla SP y no valores de SNOMBRE de la tabla S y por esta razón es preciso poder hacer dos referencias simultáneas pero distintas a la tabla SP. Ahora ya podemos desechar la tabla TEMP. Muchas veces es conveniente manejar consultas complejas con éste método de paso por paso, pues se facilita la comprensión. Pero desde luego es posible también expresar toda la consulta como una sola proposición SELECT, con lo cual resulta del todo innecesaria la tabla TEMP. SELECT DISTINCT S# FROM SP SPX WHERE NOT EXISTS ( SELECT * WHERE S# = ‘P2’ AND NOT EXISTS ( SELECT * FROM SP SPZ WHERE SPZ.S# = SPX.S# AND SPZ.P# = SPY.P#)); 6.5.11 Consulta con unión Obtener todos los números de las partes que pesen más de 16 libras, o sean suministradas por el proveedor P2 ( o las dos cosas) SELECT P# FROM P WHERE PESO>16 UNION SELECT P# FROM SP WHERE S# = ‘P2’; Resultado -----P1 P2 P3 P6 UNION es el operador de unión de la teoría de conjuntos tradicional. En otras palabras, A UNION B ( donde A y B son conjuntos) es el conjunto de todos los objetos de x tales que x es miembro de A o x es miembro e B ( o las dos cosas). Las filas repetidas siempre se eliminan del resultado de una UNION.* 6.6 OPERACIONES DE ACTUALIZACIÓN El lenguaje de manipulación de SQL, incluye tres operaciones de actualización: UPDATE(actualizar en el sentido de alterar o modificar), DELETE (eliminar) e INSERT (insertar). UPDATE (actualizar) Formato general: UPDATE Tabla SET campo = expresión-escalar [campo = expresión-escalar] … WHERE [condición]; Todos los registros de tabla que satisfagan condición serán modificados de acuerdo con las asignaciones (“ campo = expresión-escalar) de la cláusula SET (establecer). 6.6.1 Modificación de un solo registro. Cambiar a amarillo el color de la parte P2, aumentar su peso en cinco e indicar que su ciudad es “desconocida” (NULL) UPDATE P SET COLOR=”.amar” PESO=PESO+5, CIUDAD= NULL WHERE P# = ‘ P2’; Para cada uno de los registros que se van a modificar ( es decir, cada registro que satisface la condición WHERE, o todos los registros si se omiten la cláusula WHERE), Las referencias en la cláusula SET a campos dentro de ese registro representan los valores que tiene esos campos antes de ejecutarse cualquiera de las asignaciones de esa cláusula SET. 6.6.2 Modificación de varios registros. Duplicar la situación de todos los proveedores situados en Londres UPDATE S SET SITUACION = 2*SITUACIÓN WHERE CIUDAD = “LONDRES”; 6.6.3 Modificación con subconsulta. Poner en ceros la cantidad enviada por todos los proveedores de Londres. UPDATE SP SET CANT = 0 WHERE “Londres” = (SELECT CIUDAD FROM S WHERE S.S# = SP.S#); 6.6.4 Modificación de varias tablas. Cambiar el número de proveedor con S2 a S9 UPDATE SET WHERE UPDATE SET WHERE S S# = “S9” S# = “S2”; SP S# = “S9” S# = “S2”; Como por definición las operaciones UPDATE (y DELETE e INSERT) modifican la base de datos, existe siempre la posibilidad de alterar en alguna forma incorrecta i violar con ello la integridad de los datos. El ejemplo ilustra este punto: La primera posición UPDATE (si fuera aceptada) haría inconsistente a la base de datos. Incluiría algunos envíos para los cuales no existiría el proveedor correspondiente. Y permanecería en ese estado en tanto no se ejecutará la segunda proposición UPDATE (en este ejemplo pasamos por alto el hecho de que, en la practica, DB2 rechazaría de todos modos la primera modificación, precisamente por este problema de integridad.) De hecho, el ejemplo ilustra un tipo muy especifico de violación de la integridad, a saber, la violación de la integridad referencial. La integridad referencial se analiza en forma detallada en el capitulo 12. DELETE (ELIMINAR) Formato general: DELETE FROM TABLA { WHERE CONDICIÓN}; Se eliminaran todos los registros de “TABLA” que satisfagan “CONDICION”. 6.6.5 Eliminación de un solo registro Eliminar el proveedor S5. DELETE FROM S WHERE S# = ‘S5’; En general, la eliminación de un proveedor puede llegar a provocara una violación de la integridad referencial (si la base de datos incluye envíos de ese proveedor; comparesé con el ejemplo 6.6.4). una vez más, la integridad referencial se analiza en el capitulo 12. 6.6.6 Eliminación de varios registros. Eliminar todos los envíos cuya cantidad sea mayor de 300. DELETE FROM SP WHERE CANT > 300; 6.6.7 Eliminación de varios registros. Eliminar todos los embarques. DELETE FROM SP; SP es todavía una tabla conocida (“ eliminar todos los registros” no equivale a desechar (DROP) la tabla), pero ahora esta vacía. 6.6.8 Eliminación con subconsulta. Eliminar todos los envíos de proveedores situados en Londres. DELETE FROM SP WHERE “Londres” = (SELECT CIUDAD FROM S WHERE S.S# = SP.S# ); INSERT (INSERTAR) Formato general: INSERT INTO TABLA [(CAMPO[CAMPO]…)] VALUES (Literal[, literal]…); O bien INSERT INTO TABLA [(campo[, campo]…)] Subconsulta; En el primer formato se inserta en “TABLA” una fila con los valores especificados en los campos especificados ( el iésimo literal en la lista de literales corresponde al iésimo campo en la lista de campos). En el segundo formato, se valúa la subconsulta y se inserta una copia del resultado ( casi siempre varias filas) en “TABLA”; la iésima columna de ese resultado corresponde a iésimo campo en la lista de campos. En ambos casos, omitir la lista de campos equivale a especificar una lista con todos los campos de la tabla en orden de izquierda a derecha ( como en caso de “SELECT *”). 6.6.9 Inserción de un solo registro Añadir la parte P7 (ciudad, Atenas, Peso, 24; nombre y color desconocidos por ahora) A la tabla P. INSERT INTO P (P#,CIUDAD,PESO) VALUES (“P7”,”Atenas”,24); Se crea un nuevo registro de parte con el número de parte, la ciudad y el peso especificados y con nulos en las posiciones del nombre y color. (suponemos, en este ejemplo, que estos campos no se definieron NOT NULL en la proposición CREATE TABLE con la cual se creó la tabla P.) el orden de izquierda a derecha con el cual se nombrar los campos en la proposición INSERT no es por fuerza el mismo con el que se especificaron el la proposición CREATE TABLE (o ALTER TABLE). 6.6.10 Inserción de un solo registro, omitiendo nombres de campos. Añadir la parte P8 ( nombre, cadena, color, rosa, peso, 14, ciudad, Niza) A la tabla P INSERT INTO P VALUES ( ‘P8’ ,’cadena’, ‘rosa’, 14, ‘Niza’); Omitir la lista de campos equivale a especificar una lista con todos los campos de la tabla, en el orden de izquierda a derecha con el cual se definieron en la proposición CREATE TABLE. Como en el caso de “SELECT *”, esta forma corta quizá sea conveniente en SQL interactivo, pero puede ser peligrosa en SQL embebido ( o sea, SQL dentro de un programa de aplicación) por que la lista de campos supuesta puede cambiar si el programa se religa y a cambiado entre tanto la definición de la tabla. 6.6.11 Inserción de un solo registro. Insertar un nuevo envío con número de proveedor S20, número de parte P20 y cantidad 1000. INSERT INTO SP (S#, P#, CANT) VALUES ( ‘S20’,’ P20’, 1000); Al igual que UPDATE y DELETE, INSERT puede provocar problemas de integridad referencial ( si no existen controles adecuados; Véase el capitulo 12). En el presente caso, DB2 rechazara el intento de inserción, por que no existe el proveedor S20 en la tabla S ni la parte P20 en la tabla. 6.6.12 Inserción de varios registros. Para cada parte suministrada, obtener el número de parte y la cantidad total suministrada, y guardar el resultado en la base de datos. CREATE TABLE TPMP ( P# CHAR (6) NOT NULL, CANTTOTAL INTEGER NOT NULL, PRIMARY KEY ( P#); CREATE UNIQUE INDEX XT ON TEMP ( P#); INSERT INTO TEMP ( P#, CANTTOTAL) SELECT P#, SUM (CANT) FROM SP GROUP PY P# Se ejecuta la proposición SELECT, igual que una selección ordinaria, pero el resultado , en vez de presentarse al usuario, se copia a la tabla TEMP. Ahora el usuario puede hacer lo que desee con esa copia: consultarla, imprimirla y hasta modificarla; ninguna de esas operaciones afectara en absoluto los datos originales. Cuando ya no se necesite, la tabla TEMP podrá desecharse. DROP TABLE TEMP; El ejemplo anterior es una ilustración perfecta de la importancia de la propiedad de cerradura en los sistemas relacionales. El ejemplo funciona precisamente porque el resultado de una proposición SELECT es otra tabla. No resultaría si el resultado fuera algo distinto. Por cierto, no es indispensable que la tabla objetivo este vacía antes de efectuar una inserción de varios registros, aunque sí lo esta en el ejemplo anterior, si no esta vacía los nuevos registros simplemente se agregarán a los ya presentes. 6.7 COMENTARIOS FINALES. Ya hemos presentado todas las características de la proposiciones de manipulación de datos de SQL que pensamos ilustrar en este libro. En términos específicos hemos descrito: La cláusula SELECT ( seleccionar) básica, incluyendo el empleo de DISTINC (distinto), literales, y expresiones escalares, y “SELECT *” La cláusula FROM (de), incluyendo el empleo de variables de recorrido El empleo de ORDER BY (ordenar por) para ordenar la tabla de resultado La cláusula WHERE (donde), incluyendo: - operaciones escalares de comparacion =,<>, >, <, <=, >= - condiciones de reunión - operadores booleanos AND,OR,NOT Las funciones de agregados COUNT(cuenta), SUM (suma), AVG (promedio), MAX(máximo), MIN (mínimo), y el empleo de la cláusulas GROUP BY (agrupar por) y HAVING (con) Las condiciones especiales [NOT] LIKE…([no] como e IS [NOT] NULL ([no] es nulo) El empleo de subconsultas y el operador de comparación [NOT] IN ([no]) en) Empleo del cuantificador existencial EXISTS (existe) (sobre todo la forma negada NOT EXISTS (no existe)) El operador UNION (unión) Las proposiciones de actualización UPDATE (modificar), DELETE ( eliminar), e INSERT (insertar) Cabe señalar que la existencia de sólo cuatro proposiciones de DML en SQL es una de las razones de la relativa facilidad del empleo de ese lenguaje. Y el hecho de que sólo haya cuatro operaciones de este tipo es una consecuencia de la sencillez de la estructura de datos relacional. Como se explico en el capitulo 4, toda la información de una base de datos relacionada se representa de la misma manera, a saber, como valores dispuestos de columnas dentro de filas pertenecientes a tablas. Como sólo hay una forma de representar cualquier cosa, sólo se requiere un operador para cada una de las cuatro funciones básicas de manipulación (recuperar, modificar, insertar, eliminar). En cambio en los sistemas basados en estructuras de datos más complejas requieren en principio 4n operaciones de este tipo, donde n es el número de formas de representar datos en ese sistema. Por ejemplo en los sistema derivados de CODASYL, donde los datos se pueden representar ya sea como registros o como ligas entre registros, lo más común es encontrar una operación STORE (almacenar) para crear un registro y una operación CONNECT (conectar) para crear una liga; una operación ERASE para eliminar un registro y una operación DISCONNECT (desconectar) para eliminar una liga,; una operación MODIFY (modificar) para alterar un registro y una operación RECONNECT (reconectar) para modificar una liga, Etc. Para concluir, presentamos un ejemplo muy rebuscado para ilustrar como pueden utilizarse muchas de las características de SQL analizadas en este capitulo. Tambien presentamos un algoritmo conceptual para evaluar una proposición de este tipo. Adviértase que en cierto sentido SELECT es la más fundamental de las cuatro operaciones de DML, pues las otras tres deben ir siempre precedidas –al menos en forma implícita, si no explícita- por una selección apropiada. Por ejemplo, en el caso de DELETE el sistema debe ejecutar antes una selección implícita para localizar los datos que debe eliminar. Ejemplo: Para todas las partes rojas y azules tales que la cantidad total suministrada sea mayor o igual que 350 (excluyendo del total todos los envíos cuyas cantidades sean menores o iguales a 200), obtener el número de parte, el peso en gramos, el color y la cantidad máxima suministrada de esa parte; y clasificar el resultado en orden descendente por número de parte dentro de un orden ascendente según esa escala máxima. SELECT P.P#, ‘peso en gramos=’, P.PESO *454, P.COLOR, ‘ cant.máx.embarcada=’, MAX (SP.CANT) FROM P, SP WHERE P.P# = SP.P# AND (P.COLOR= ‘rojo’ OR P.COLOR = ’azul ’) AND SP.CANT > 200 GROUP BY P.P# , P.PESO, P.COLOR HAVING SUM (CANT) >350 ORDER BY 6, P.P# DESC; Resultado: P# P1 P5 P3 peso en gramos = 5448 peso en gramos = 5448 peso en gramos = 7718 COLOR rojo cant. Máx enviada = 300 azul cant. Máx enviada = 400 azul cant. Máx enviada = 400 Debe entenderse que el algoritmo recién descrito se presenta como una explicación puramente conceptual de la forma como se evalúa la proposición SELECT (seleccionar). Sin duda es correcta, en cuanto a que se producirá con certeza el resultado correcto; de hecho, puede tomarse como definición de cual debe ser el resultado. No obstante si se le ejecutara en realidad, lo más probable es que fuera bastante ineficiente. Por ejemplo, no sería conveniente, por razones de espacio de almacenamiento y también el tiempo de ejecución, la construcción del producto cartesiano sugerida en el paso 1. Consideraciones como ésta son precisamente las que justifican la necesidad de un optimizador de sistema relacionales. De hecho, el objetivo del optimizador puede definirse como encontrar un procedimiento que produzca el mismo resultado producido por el algoritmo conceptual antes descrito pero que sea más eficiente en términos de espacio o de tiempo ( o de ambos, si es posible). Por último, acerca del ejemplo de consulta en sí: la proposición SELECT mostrada es desde luego bastante compleja, pero pensemos en todo el trabajo que realiza. Un programa convencional para efectuar la misma tarea, escrito en el lenguaje de COBOL, podría ocupar con facilidad nueve páginas, en vez de solo nueve renglones, y la labor requerida para poner en funcionamiento ese programa sería bastante mayor que la implicada en la construcción de la versión de SQL mostrada. En la practica, por supuesto, la mayor parte de las consultas serán de todos modos más sencillas que ésta. Ejercicios. Todos los ejercicios utilizan la base de datos de proveedores, partes y proyectos (véanse los ejercicios del capitulo 5). En casi todos se pide al lector que escriba una proposición o conjunto de proposiciones en SQL para la operación indicada. Por conveniencia repetimos aquí la estructura de la base de datos. S ( S#, SNOMBRE, SITUACION, CIUDAD) P (P#, PNOMBRE, COLOR,PESO,CIUDAD) J (J#, JNOMBRE,CIUDAD) SPJ (S#, P#, J#, CANT) Dentro de cada subsección, los ejercicios se presentan en orden aproximado de dificultad creciente. Se recomienda intentar al menos algunos de los más difíciles en cada grupo. Los números 34 a40 son bastante difíciles. Consultas sencillas: 6.1 Obtener los detalles completos de los proyectos. 6.2 Obtener los detalles completos de los proyectos de Londres 6.3 Obtener los números de los proveedores que suministran partes al proyecto J1 , ordenados por el número del proveedor. 6.4 Obtener todos los envíos en los cuales la cantidad está en el intervalo de 300 a 750 inclusive. 6.5 Obtener una lista de todas las combinaciones parte-color / parte-ciudad, eliminando todas las parejas color / ciudad repetidas. Reuniones. 6.6 Obtener todas las tripletas número de proveedor /número de parte/ número de proyecto tales que el proveedor, la parte y el proyecto indicados estén en la misma ciudad. 6.7 Obtener todas las tripletas número de proveedor /número de parte/ número de proyecto tales que el proveedor, la parte y el proyecto indicados no estén todos cosituados. 6.8 obtener todas las tripletas número de proveedor /número de parte/ número de proyecto tales que el proveedor, la parte y el proyecto indicados estén todos en diferente ciudad. 6.9 Obtener los números de las partes suministradas por algún proveedor de Londres. 6.10 Obtener los números de las partes suministradas por algún proveedor de Londres a un proyecto en Londres. 6.11 obtener todas las parejas de nombres de ciudades tales que un proveedor de a primera ciudad suministre partes a un proyecto en la segunda ciudad. 6.12 obtener los números de las partes suministradas a un proyecto por un proveedor situado en la misma ciudad que en el proyecto. 6.13 obtener los números de los proyectos a los cuales suministra partes por lo menos un proveedor situado en diferente ciudad. 6.14 obtener todas las parejas de números de partes tales que algún proveedor suministre dos partes indicadas. FUNCIONES AGREGADAS 6.15 obtener el número total de proyectos a los cuales suministra partes el proveedor S1 6.16 obtener la cantidad total de la parte P1 suministrada por el proveedor S1 6.17 para cada parte suministrada a un proyecto, obtener el número de parte, el número de proyecto y la cantidad total correspondiente. 6.18 obtener los números de las partes suministradas a algún proyecto tales que la cantidad promedio suministrada sea mayor que320. DIVERSAS 6.19 obtener todos los envíos para los cuales la cantidad no sea nula 6.20 obtener número de proyecto y ciudades en los cuales la segunda letra del nombre de la ciudad sea una “o”. 6.21 obtener los nombres de los proyectos a los cuales suministra partes el proveedor S1 6.22 Obtener los colores de las partes suministradas por el proveedor S1 6.23 Obtener los números de las partes suministradas a cualquier proyecto en Londres 6.24 Obtener los números de los proyectos donde se utilice al menos una de las partes suministradas por el proveedor S1. 6.25 obtener los números de los proveedores que suministren por lo menos unas de las partes suministradas por al menos uno de los proveedores que suministran por lo menos una parte roja. 6.26 obtener los números de proveedores cuya situación sea inferior a la del proveedor S1. 6.27 obtener los números de los proyectos cuya ciudad sea la primera en la lista alfabética de las ciudades donde hay proyectos. 6.28 Obtener los números de los proyectos a los cuales se suministre la parte P1 en una cantidad promedio mayor que la cantidad máxima en la cual se suministra alguna parte al proyecto J1 6.29 Obtener los números de los proveedores que suministren la parte P1 a algún proyecto en una cantidad mayor que la cantidad promedio enviada de la parte P1 para ese proyecto. EXISTS (existe) 6.30 Repetir el ejercicio 6.23 utilizando exists en la solución. 6.31 repetir el ejercicio 6.24 utilizando exists en la solución. 6.32 obtener los números de los proyectos a los cuales no suministra ninguna parte roja ninguno de los proveedores de Londres. 6.33 Obtener los números de los proyectos para los cuales S1 es el único proveedor 6.34 Obtenr los números de las partes suministradas a todos los proyectos en nombres. 6.35 Obtener los números de los proveedores que suministren la misma parte a todos los proyectos. 6.36 Obtener los números de los proyectos a los cuales se suministren por lo menos todas las partes suministradas por el proveedor S1. En los siguientes cuatro ejercicios (6.37 a 6.40) convertir la proporción SELECT de SQL mostrada a su equivalente en español. 6.37 SELECT DISTINC J# FROM SPJ SPJX WHERE NOT EXISTS (SELECT * FROM SPJ SPJX WHERE SPJX.J# = SPJX.J# AND NOT EXISTS (SELECT * FROM SPJ SPJZ WHERE SPJZ.P# = SPJY.P# AND SPJZ.S# = ‘S1’ )); 6.38 SELECT DISTINC J# FROM SPJ SPJX WHERE NOT EXISTS (SELECT * FROM SPJ SPJX WHERE EXISTS (SELECT * FROM SPJ SPJA WHERE SPJA.S# = ‘S1’ AND SPJA.P# = SPJY.P#) AND NOT EXISTS ( SELECT * FROM SPJ SPJB WHERE SPJB.S# = ‘S1’ AND SPJB.P# = SPJY.P# AND SPJB.J# = SPJX.J#)); 6.39 SELECT DISTINC J# FROM SPJ SPJX WHERE NOT EXISTS (SELECT * FROM SPJ SPJY WHERE EXISTS (SELECT * FROM SPJ SPJA WHERE SPJA.P# = SPJY.P# AND SPJA.J# = SPJX.J#) AND NOT EXISTS (SELECT * FROM SPJ SPJB WHERE SPJB.S# = ’S1’ AND SPJB.P# = SPJY.P# AND SPJB.J# = SPJX.J#)); 6.40 SELECT DISTINC J# FROM SPJ SPJX WHERE NOT EXISTS (SELECT * FROM SPJ SPJY WHERE EXISTS (SELECT * FROM SPJ SPJA WHERE SPJA.S# = SPJY.S# AND SPJA.P# IN (SELECT P# FROM P WHERE COLOR = ’rojo’) AND NOT EXISTS (SELECT * FROM SPJ SPJB WHERE SPJB.S# = SPJY.S# AND SPJB.J# = SPJX.J#))); UNION 6.41 Construir una lista ordenada de todas las ciudades en las cuales este situado por lo menos un proveedor, una parte o un proyecto. 6.42 Mostrar el resultado de la siguiente selección: SELECT P.COLOR FROM P UNION SELECT P.COLOR FROM P. OPERACIONES DE ACTUALIZACION 6.43 Cambiar a gris el color de todas las partes rojas 6.44 Eliminar todos los proyectos para los cuales no haya envíos. 6.45 Insertar un nuevo proveedor (S10) en la tabla S. El nombre y la ciudad son Salazar y Nueva York, respectivamente; la situación no se conoce todavía. 6.46 Construir una tabla con los números de las partes suministradas ya sea por un proveedor de Londres o un proyecto en Londres. 6.47 Construir una tabla con los números de los proyectos situados en Londres a los cuales suministren partes algún proveedor de Londres. RESPUESTA A EJERCICIOS SELECTOS. Las siguientes soluciones no son por fuerza las únicas posibles. 6.1 SELECT J#, JNOMBRE, CIUDAD FROM J; 6.2 SELECT J#, JNOMBRE, CIUDAD FROM J WHERE CIUDAD = ‘Londres’; 6.3 SELECT DISTINC S# FROM SPJ WHERE J# = ‘J1’ ORDER BY S#; 6.4 SELECT S#, P#, J# CANT FROM SPJ WHERE CANT > = 300 AND CANT <= 750 6.5 SELECT DISTINC COLOR, CIUDAD FROM P; 6.6 SELECT S#, P#, J# FROM S, P, J WHERE S.CIUDAD=P.CIUDAD AND P.CIUDAD= J.CIUDAD; 6.7 SELECT S#, P#, J# FROM S,P,J WHERE NOT (S.CIUDAD = P.CIUDAD AND P.CIUDAD = J.CIUDAD ); 6.8 SELECT S#, P#, J# FROM P,J WHERE S.CIUDAD <> P.CIUDAD AND P.CIUDAD <> J.CIUDAD AND J.CIUDAD <> S.CIUDAD; 6.9 SELECT DISTINC FROM SPJ, S WHERE SPJ.S# = S.S# AND CIUDAD =’ LONDRES 6.10 SELECT DISTINC P* FROM SPJ, S, J WHERE SPJ. S# = S.S# AND SPJ.J# = J.J# AND S.CIUDAD = ‘ LONDRES’ AND J.CIUDAD = ‘LONDRES’; 6.11 SELECT DISTINC S.CIUDAD, J.CIUDAD FROM S, SPJ, J WHERE S.S# SPJ.S# AND SPJ.J# = J.J#; 6.12 SELECT DISTINC P# FROM SPJ, S, J WHERE SPJ.S# = S.S# AND SPJ.J# = J.J# AND S.CIUDAD <> J.CIUDAD; 6.14 SELECT SPJX.P#, SPJY.P# FROM SPJX, SPJ, SPJY WHERE SPJX.S# = SPJY.S# AND SPJX.P# > SPJY.P#; 6.15 SELECT COUNT (DISTINC J#) FROM SPJ WHERE S# = ‘S1’; 6.16 SELECT SUM (CANT) FROM SPJ WHERE P# = ‘P1’ AND S# = ‘S1’; 6.17 SELECT P#, J#, SUM (CANT) FROM SPJ GRUOP BY P#, J#; 6.18 SELECT DISTINC P# FROM SPJ GROUP BY P#, J# HAVING AVG (CANT) > 320; 6.19 SELECT S#, P#, J#, CANT FROM SPJ WHERE CANT IS NOT NULL; 6.20 SELECT J#, CIUDAD FROM J WHERE CIUDAD LIKE ‘_o%’; 6.21 SELECT JNOMBRE FROM J WHERE J# (SELECT J# FROM SPJ WHERE S# = ‘S1’);