SQL Avanzado José Muñoz Jimeno Febrero 2015 Control de cambios Versión Fecha Comentarios 1.0 13/02/2015 Primera versión para el curso “Introducción a las bases de datos con MySQL” en el COITCV La última versión de este documento puede descargarse en http://mestreacasa.gva.es/web/munoz_jos24/cursos Índice Combinaciónes de tablas Producto cartesiano Combinación o reunión Combinación externa Subconsultas En el FROM En WHERE o HAVING En SELECT Consultas de unión Sentencias SELECT avanzadas Combinaciones de tablas Consultas que toman datos de más de una tabla Subconsultas Tendremos una consulta SQL dentro de otra Consultas de unión Uniremos más de una sentencia SQL Combinaciones de tablas. Hasta ahora FROM <tabla> → ahora FROM <tabla s> Hay varias formas de usar más de una tabla: Producto cartesiano → no útil. Conocer, para evitarla Combinación o reunión → la más utilizada Combinación externa → variante de la anterior. Muy útil. Combinaciones de tablas Producto cartesiano SELECT … FROM tablaA, tablaB Producto cartesiano o cross join Cada registro de una relación se combina con TODOS los de la otra Cualificar: ahora hemos de #filas_resultado = #filasA x #filas B decir de qué tabla es cada Ejemplo: campo si hay campos SELECT Empleat.Nom, Familiar.Nom iguales FROM Empleat, Familiar; Resultado: 70 filas: 7 Empleat x 10 Familiar No proporciona ninguna información útil Combinaciones de tablas Producto cartesiano SELECT Empleat.Nom, Familiar.Nom FROM Empleat,Familiar; Resultado: 70 filas: 7 Empleat x 10 Familiar No proporciona ninguna información útil Ojo: la BD lo hace en otras combinaciones antes de quedarse con las filas que le interesan → ver orden magnitud consultas Combinaciones de tablas Combinación interna SELECT ... FROM tabla1 INNER JOIN tabla2 ON tabla1.campo1 = tabla2.campo2 Combinación interna o combinación o reunión Nos interesa combinar tablas que coincidan en un campo de cada una Generalmente una clave externa y la principal a la que apunta Ej: Combinar cada empleado con los familiares que tiene SELECT Empleat.Nom, Familiar.Nom FROM Empleat INNER JOIN Familiar ON Empleat.Dni = Familiar.Dni; Combinaciones de tablas Combinación interna Sintaxis alternativa: SELECT ... FROM tabla1, tabla2 WHERE tabla1.campo1 = tabla2.campo2; Es como hacer un producto cartesiano y luego quedarnos con las filas que nos interesan Es más utilizada esta sintaxis, pero es cuestión de gustos Ejemplo SELECT Empleat.Nom, Familiar.Nom FROM Empleat, Familiar WHERE Empleat.Dni = Familiar.Dni; Combinaciones de tablas Combinación interna - Ejemplos Sacar el nombre de los departamentos y sus empleados SELECT nom_d,nom FROM Departament INNER JOIN Empleat ON Departament.num_d=Empleat.departament; Sacar de cada departamento su nombre y el de su director SELECT nom_d, nom FROM Departament INNER JOIN Empleat ON Departament.director=Empleat.dni; Sacar el nombre de los departamentos y el número de empleados de cada uno SELECT nom_d, COUNT(dni) AS 'Num_emp' FROM Departament INNER JOIN Empleat ON Departament.num_d=Empleat.departament GROUP BY nom_d; Combinaciones de tablas Combinación interna - 3 ó más tablas Ej: Nombre de un departamento, de sus empleados y el nombre de sus familiares. SELECT nom_d, Empleat.nom, Familiar.nom AS Nom_fam FROM (Departament INNER JOIN Empleat ON Departament.num_d=Empleat.departament) INNER JOIN Familiar ON Empleat.dni=Familiar.dni; o bien SELECT nom_d, Empleat.nom, Familiar.nom FROM Departament , Empleat , Familiar WHERE Departament.num_d=Empleat.departament AND Empleat.dni=FAMILIAR.dni; En general n tablas → n-1 condiciones de combinación Combinaciones de tablas Combinación interna - 3 ó más tablas - Ejemplo Obtener nombre de los empleados con el nombre de los proyectos en los que trabajan. Necesitamos: Empleat.nom y Projecte.nom_p ¡Pero Empleat y Projecte no se pueden combinar! Necesitamos Treballa, aunque no mostremos nada de ella SELECT Empleat.nom, Projecte.nom_p FROM (Empleat INNER JOIN Treballa ON Empleat.dni=Treballa.dni) INNER JOIN Projecte ON Treballa.num_p=Projecte.num_p; o bien SELECT Empleat.nom, Projecte.nom_p FROM Empleat, Treballa, Projecte WHERE Empleat.dni=Treballa.dni AND Treballa.num_p=Projecte.num_p; Combinaciones de tablas Combinación interna - Una tabla más de una vez Ej: Queremos obtener el nombre de los empleados y su supervisor ¡Está en la misma tabla! ¿Cómo la ponemos 2 veces? Renombrándolas: FROM tabla [AS] T1 SELECT E1.nom AS 'Empleado' , E2.nom as Supervisor FROM Empleat E1 INNER JOIN Empleat E2 ON E1.supervisor=E2.dni; O bien SELECT E1.nom AS 'Empleado' , E2.nom as Supervisor FROM Empleat E1, Empleat E2 WHERE E1.supervisor=E2.dni; Combinaciones de tablas Combinación interna - Clave externa formada por más de un campo Hemos de combinar por todos los campos Ej: Cuenta corriente depende en identificación de Sucursal Clave ppal de Sucursal 2 campos: n_ent, n_suc Clave externa de CC: los dos campos SELECT C.n_ent , C.n_suc , n_cc , S.nom , C.saldo FROM SUCURSAL S INNER JOIN COMPTE_CORRENT C ON S.n_ent=C.n_ent AND S.n_suc=C.n_suc; SELECT C.n_ent , C.n_suc , n_cc , S.nom , C.saldo FROM SUCURSAL S, COMPTE_CORRENT C WHERE S.n_ent=C.n_ent AND S.n_suc=C.n_suc Nota: se ha usado S y C para acortar Combinaciones de tablas Combinación externa SELECT ... FROM tabla1 LEFT JOIN tabla2 ON tabla1.campo1 = tabla2.campo2 Obtendremos todas las entradas de la tabla 1 y aquellas que estén relacionadas de la tabla2 Si cambiamos LEFT por RIGHT obtendremos todas las de tabla2 y las relacionadas de tabla1 Ejemplo SELECT Empleat.nom, Familiar.nom AS Nom_fam FROM Empleat LEFT JOIN Familiar ON Empleat.dni=Familiar.dni Combinaciones de tablas Combinación externa - Ejemplos Obtener el nombre de los empleados con el número de familiares que tiene cada uno → contar clave ppal Familiar SELECT Empleat.nom, COUNT(Familiar.dni) FROM Empleat LEFT JOIN Familiar ON Empleat.dni=Familiar.dni GROUP BY Empleat.nom; Obtener los nombres de los Departamentos i los nombres de los Proyectos coordinados por cada uno, incluyendo Departamentos que no coordinan ningún proyecto SELECT nom_d,nom_p FROM Departament LEFT JOIN Projecte ON Departament.num_d=Projecte.departament; Combinaciones de tablas Combinación externa - Ejemplos Obtener el nombre de todos los empleados y el número de empleados a los que supervisan SELECT E1.nom, COUNT(E2.dni) AS 'Num supervisados' FROM Empleat E1 LEFT JOIN Empleat E2 ON E1.dni=E2.supervisor GROUP BY E1.nom; Obtener los empleados sin familiares SELECT Empleat.nom FROM Empleat LEFT JOIN Familiar ON Empleat.dni=Familiar.dni WHERE Familiar.dni IS NULL; Combinaciones de tablas Combinación externa - Ejemplos Obtener el nombre de todos los departamentos, el de sus empleados y el de sus familiares. SELECT nom_d, Empleat.nom, Familiar.nom FROM (Departament LEFT JOIN Empleat ON Departament.num_d=Empleat.departament) LEFT JOIN Familiar ON Empleat.dni=Familiar.dni ORDER BY 1,2,3; Subconsultas Una consulta dentro de otra consulta Puede ir en: FROM Será el origen de datos. Dará los datos a la consulta principal. WHERE o HAVING Formará parte de una condición. Podremos comparar, en la consulta principal, un campo con lo que devuelva la subconsulta Tendremos operadores y predicados especiales SELECT Para obtener un resultado global que no afectará al resto de la consulta principal Subconsultas En el FROM SELECT ... FROM ( Subconsulta ) Ej: Queremos obtener la media de empleados por departamento. Obtenemos número de empleados por departamento y después obtenemos la media SELECT avg( Cuantos ) FROM ( SELECT COUNT( * ) AS Cuantos FROM Empleat GROUP BY Departament) AS C; Se ejecuta de dentro a fuera → crear primero subconsulta Necesario alias en campo de la subconsulta para referenciarla Hemos de dar un nombre a la subconsulta para que funcione Subconsultas En el WHERE o HAVING SELECT ... FROM Tabla WHERE campo operador ( Subconsulta ) Comparamos un campo de la tabla con el resultado de una subconsulta → puede usar la misma tabla u otras Ej: Obtener empleados que cobran más que la media SELECT * FROM Empleat WHERE sou > (SELECT AVG(sou) FROM Empleat); En estas no hace falta dar nombre a la subconsulta Subconsulta devuelve 1 valor. Comparamos sou con ese valor Subconsultas En el WHERE o HAVING Operadores que pueden usarse: De comparación → =, <, >, >=, <=, <>, ALL, ANY, SOME Operador IN Operador EXISTS Subconsultas En el WHERE o HAVING - Operadores comparación De comparación =, <, >, >=, <=, <> → Si subconsulta devuelve 1 sólo valor ALL, ANY, SOME → Subconsulta devuelve varios valores ANY y SOME→cualquiera (sinónimos) ALL→ Todos SELECT * FROM Empleat WHERE sou = (SELECT MAX(sou) FROM Empleat GROUP BY departament); ¿Funcionará? No, ¿con qué compara el sou de cada empleat? SELECT * FROM Empleat WHERE sou = ANY (SELECT MAX(sou) FROM Empleat GROUP BY departament); Funcionará para cualquier sou que sea igual que alguno de los más altos de cada departamento Subconsultas En el WHERE o HAVING - Operador IN La comparación será cierta si el valor del campo está entre los devueltos por la subconsulta También podemos usar NOT IN Ej: Empleados que no tienen familiares SELECT * FROM Empleat WHERE dni NOT IN ( SELECT dni FROM Familiar); Subconsultas En el WHERE o HAVING - Operador EXISTS El más “incómodo” No se compara un campo con el resultado de la subconsulta Se pone [NOT] EXISTS subconsulta Condición será cierta si subconsulta devuelve alguna fila Ejemplo anterior: empleados sin familiares Hemos de conseguir que subconsulta no devuelva filas para los empleados sin familiares De palabra: empleados para los que no existe ningún registro en Familiar con su dni En subconsulta SELECT * hacemos referencia a FROM Empleat campo de la principal WHERE NOT EXISTS (SELECT * Se va ejecutando la subconsulta para FROM Familiar cada fila de la WHERE dni= Empleat.dni); principal ● ● Subconsulta En el SELECT SELECT ... ( Subconsulta ) FROM Tabla La subconsulta se usa para obtener UN valor a usar en el select Ej: Obtener la diferencia del sueldo de cada empleado con la media SELECT nom, sou, sou-(SELECT AVG(sou) FROM Empleat) FROM Empleat; Subconsultas Ejemplos Obtener el número de horas máximo y mínimo destinadas a un proyecto SELECT MAX(cuantas),MIN(cuantas) FROM (SELECT SUM(hores) AS cuantas FROM Treballa GROUP BY num_p) AS T; Obtener empleados sin descendientes (fill o filla) SELECT * FROM Empleat WHERE dni NOT IN (SELECT DISTINCT dni FROM Familiar WHERE parentesc LIKE 'fill%'); Subconsultas Ejemplos Obtener toda la información de los empleados que cobran más y menos en la empresa SELECT * FROM Empleat WHERE sou = (SELECT MAX(sou) FROM Empleat) OR sou = (SELECT MIN(sou) FROM Empleat); Obtener los empleados con un sueldo mayor que la media de su departamento La subconsulta se ejecuta una vez para cada registro de la consulta principal SELECT nom, departament, sou FROM Empleat E1 WHERE sou > (SELECT Avg(sou) FROM Empleat WHERE departament= E1.departament); Subconsultas Ejemplos Obtener dni y nombre de los empleados para los cuales la media de horas trabajadas en proyectos, es superior a la media general de horas trabajadas en proyectos. SELECT Empleat.dni, nom, AVG(hores) AS TotalHores FROM Empleat,Treballa WHERE Empleat.dni = Treballa.dni GROUP BY Empleat.dni, nom HAVING AVG(hores) > (SELECT AVG(hores) FROM Treballa); Subconsultas Ejemplos Obtener el nombre de departamento, el número de empleados y el % que supone respecto al total de empleados. SELECT nom_d, COUNT(dni), COUNT(dni)*100/(SELECT COUNT(*) FROM Empleat) FROM Departament, Empleat WHERE num_d=departament GROUP BY nom_d; Consultas de unión [TABLE] consulta1 UNION [ALL] [TABLE] consulta2 … Mostrará las filas de una consulta a continuación de la otra Si queremos usar una tabla ponemos TABLE nombre_tabla Ambas consultas deben devolver Mismo número de campos De tipos compatibles Si hay una fila repetida en una o más consultas la mostrará 1 sola vez → si las queremos todas poner ALL Nombres de los campos los da primera consulta Si queremos ordenar, después de la última consulta Consultas de unión Ejemplo Obtener fecha nacimiento y nombre de empleados y familiares SELECT Nom, Data_n FROM Empleat UNION SELECT Nom, Data_n FROM Familiar ORDER BY data_n;