DIPLOMADO EN ADMINISTRACION DE ORACLE
(NIVEL BASICO)
FUNCIONES DE AGRUPACION
Y
CONSULTAS MULTIPLES
PREPARADO POR:
JUAN A. BARCASNEGRAS G.
FUNCIONES DE AGRUPACION
En Oracle hay dos tipos de funciones: funciones de fila única (single row functions) y funciones
de agrupación (aggregate functions). La diferencia fundamental es que mientras las primeras
realizan la acción sobre una única fila cada vez, las de agrupación obtienen un resultado a
partir de un conjunto de elementos.
Se trata de seleccionar un conjunto de elementos, filtrarlos por las condiciones que creamos
oportunas y obtener un resultado a partir de él. ¿Cuál es el salario medio de los trabajadores
de una empresa? ¿Y el máximo por departamento? ¿Cómo obtener un determinado percentil
de una distribución? Todo esto se consigue gracias a este tipo de funciones.
Tomemos la más típica de las funciones de agrupación: COUNT. Esta función sirve para contar
el número de registros o filas que recuperamos de la base de datos. Por ejemplo, para contar
todos los empleados de una empresa (el * indica que tomamos toda la fila):
SELECT COUNT(*)
FROM EMPLEADOS
También en Oracle es muy común utilizar consultas en las que se desee agrupar los datos a fin
de realizar cálculos en vertical, es decir calculados a partir de datos de distintos registros. Para
ello se utiliza la cláusula GROUP BY que permite indicar en base a qué registros se realiza la
agrupación. Con GROUP BY la instrucción SELECT queda de esta forma:
SELECT listaDeExpresiones
FROM listaDeTablas
[JOIN tablasRelacionadasYCondicionesDeRelación]
[WHERE condiciones]
[GROUP BY grupos]
[HAVING condiciones de grupo]
[ORDER BY columnas];
Tomando en cuenta el ejemplo anterior, podemos entonces realizarnos la siguiente pregunta:
¿Cómo filtrar y conseguir el número de empleados por departamento? Para ello debemos crear
la agrupación, que en este caso se especificará indicando el código del departamento al que
pertenece el empleado. Dicho de otra forma: creamos conjuntos de empleados por
departamento y luego contamos cada conjunto. Algo así:
SELECT COUNT(*), DEPARTAMENTO
FROM EMPLEADOS
GROUP BY DEPARTAMENTO
No se puede contar algo que no está agrupado. El código siguiente es erróneo:
SELECT COUNT(*), DEPARTAMENTO
FROM EMPLEADOS
En el apartado GROUP BY, se indican las columnas por las que se agrupa. La función de este
apartado es crear un único registro por cada valor distinto en las columnas del grupo. Si por
ejemplo agrupamos en base a las columnas tipo y modelo en una tabla de existencias, se
creará un único registro por cada tipo y modelo distintos:
SELECT tipo,modelo
FROM existencias
GROUP BY tipo,modelo;
Si la tabla de existencias sin agrupar es:
TIPO MODELO N_ALMACEN CANTIDAD
AR 6 1 2500
AR 6 2 5600
AR 6 3 2430
AR 9 1 250
AR 9 2 4000
AR 9 3 678
AR 15 1 5667
AR 20 3 43
BI 10 2 340
BI 10 3 23
BI 38 1 1100
BI 38 2 540
La consulta anterior creará esta salida:
TIPO MODELO
AR 6
AR 9
AR 15
AR 20
BI 10
BI 38
Es decir es un resumen de los datos anteriores. Los datos n_almacen y cantidad no están
disponibles directamente ya que son distintos en los registros del mismo grupo. Sólo se pueden
utilizar desde funciones (como se verá ahora). Es decir esta consulta es errónea:
SELECT tipo, modelo, cantidad
FROM existencias GROUP BY tipo, modelo;
SELECT tipo, modelo, cantidad
*
ERROR en línea 1:
ORA-00979: no es una expresión GROUP BY
Retomando el primer ejemplo, si queremos filtrar por la función de agrupación, debemos utilizar
la cláusula HAVING. Utilizar WHERE nos producirá un error. Por ejemplo, si queremos contar
los empleados de los departamentos que tengan más de 10 empleados:
SELECT COUNT(*), DEPARTAMENTO
FROM EMPLEADOS
GROUP BY DEPARTAMENTO
HAVING COUNT(*) > 10
En fin, que se puede complicar tanto como necesitemos. Sólo un consejo: hay que tener en
cuenta el tipo de datos sobre el que se desea realizar la agrupación. No es lo mismo obtener el
máximo de una columna numérica (10 es mayor que 9, por ejemplo) que de una alfanumérica
(alfabéticamente, la cadena de caracteres ‘10′ es menor que la cadena ‘9′, por ejemplo).
CONSULTAS MULTIPLES
Es más que habitual necesitar en una consulta datos que se encuentran distribuidos en varias
tablas. Las bases de datos relacionales se basan en que los datos se distribuyen en tablas que
se pueden relacionar mediante un campo. Ese campo es el que permite integrar los datos de
las tablas.
Por ejemplo si disponemos de una tabla de empleados cuya clave es el ID y otra tabla de
tareas que se refiere a tareas realizadas por los empleados, es seguro (si el diseño está bien
hecho) que en la tabla de tareas aparecerá el ID del empleado para saber quién fue el
empleado realizó la tarea.
En el ejemplo anterior si quiere obtener una lista de los datos de las tareas y los empleados, se
podría hacer de esta forma:
SELECT cod_tarea, descripcion_tarea, id_empleado, nombre_empleado
FROM tareas,empleados;
La sintaxis es correcta ya que, efectivamente, en el apartado FROM se pueden indicar varias
tareas separadas por comas. Pero eso produce un producto cruzado, aparecerán todos los
registros de las tareas relacionados con todos los registros de empleados.
El producto cartesiano a veces es útil para realizar consultas complejas, pero en el caso normal
no lo es, necesitamos discriminar ese producto para que sólo aparezcan los registros de las
tareas relacionadas con sus empleados correspondientes. A eso se le llama asociar (JOIN)
tablas.
La forma de realizar correctamente la consulta anterior (asociado las tareas con los empleados
que la realizaron sería:
SELECT cod_tarea, descripcion_tarea, id_empleado,
nombre_empleado
FROM tareas,empleados
WHERE tareas.id_empleado = empleados.id;
Nótese que se utiliza la notación tabla.columna para evitar la ambigüedad, ya que el mismo
nombre de campo se puede repetir en ambas tablas. Para evitar repetir continuamente el
nombre de la tabla, se puede utilizar un alias de tabla:
SELECT a.cod_tarea, a.descripcion_tarea, b.id_empleado, b.nombre_empleado
FROM tareas a,empleados b
WHERE a.id_empleado = b.id;
Al apartado WHERE se le pueden añadir condiciones encadenándolas con el operador AND:
SELECT a.cod_tarea, a.descripcion_tarea
FROM tareas a,empleados b
WHERE a.id_empleado = b.id AND b.nombre_empleado = 'Javier';
Finalmente indicar que se pueden enlazar más de dos tablas a través de sus campos
relacionados:
SELECT a.cod_tarea, a.descripcion_tarea, b.nombre_empleado, c.nombre_utensilio
FROM tareas a,empleados b, utensilios_utilizados c
WHERE a.dni_empleado = b.dni AND a.cod_tarea=c.cod_tarea;
A las relaciones descritas anteriormente se las llama relaciones en igualdad (equijoins), ya que
las tablas se relacionan a través de campos que contienen valores iguales en dos
tablas.
A veces esto no ocurre, en las tablas:
EMPLEADOS
EMPLEADO SUELDO
Antonio 18000
Marta 21000
Sonia 15000
Andrés 11000
…….
CATEGORÍA SUELDO MÍNIMO SUELDO MÁXIMO
D 6000 11999
C 12000 17999
B 18000 20999
A 20999 80000
En el ejemplo anterior podríamos averiguar la categoría a la que pertenece cada empleado,
pero estas tablas poseen una relación que ya no es de igualdad.
La forma sería:
SELECT a.empleado, a.sueldo, b.categoria
FROM empleados a, categorias b
WHERE a.sueldo between b.sueldo_minimo and b.sueldo_maximo;
En el ejemplo visto anteriormente de las tareas y los empleados. Podría ocurrir que un
empleado no hubiera realizado una tarea todavía, con lo que habría empleados que no
aparecerían en la consulta al no tener una tarea relacionada.
La forma de conseguir que salgan todos los registros de una tabla aunque no estén
relacionados con las de otra es realizar una asociación lateral o unión externa (también llamada
outer join). En esas asociaciones, el signo (+) indica que se desean todos los registros de la
tabla estén o no relacionados.
SELECT tabla1.columna1, tabla1.columna2, … tabla2.columna1, tabla2.columna2,...
FROM tabla1, tabla2
WHERE tabla1.columnaRelacionada(+)=tabla2.columnaRelacionada
Eso obtiene los registros relacionados entre las tablas y además los registros no relacionados
de la tabla2. Se podría usar esta otra forma:
SELECT tabla1.columna1, tabla1.columna2, … tabla2.columna1, tabla2.columna2,...
FROM tabla1, tabla2
WHERE tabla1.columnaRelacionada=tabla2.columnaRelacionada(+)
En ese caso salen los relacionados y los de la primera tabla que no estén relacionados con
ninguno de la primera.
En la versión SQL de 1999 se ideó una nueva sintaxis para consultar varias tablas. La razón
fue separar las condiciones de asociación respecto de las condiciones de selección de
registros. La sintaxis completa es:
SELECT tabla1.columna1, tabl1.columna2, … tabla2.columna1, tabla2.columna2, …
FROM tabla1
[CROSS JOIN tabla2]|
[NATURAL JOIN tabla2]|
[JOIN tabla2 USING(columna)]|
[JOIN tabla2 ON (tabla1.columa=tabla2.columna)]|
[LEFT|RIGHT|FULL OUTER JOIN tabla2 ON
(tabla1.columa=tabla2.columna)]
Se describen sus posibilidades
CROSS JOIN: Utilizando la opción CROSS JOIN se realiza un producto cruzado entre las
tablas indicadas
NATURAL JOIN: Establece una relación de igualdad entre las tablas a través de los campos
que tengan el mismo nombre en ambas tablas:
SELECT * FROM piezas
NATURAL JOIN existencias;
En el ejemplo anterior se obtienen los registros de piezas relacionados en existencias a través
de los campos que tengan el mismo nombre en ambas tablas
JOIN USING: Permite establecer relaciones indicando qué campo (o campos) común a las dos
tablas hay que utilizar:
SELECT * FROM piezas
JOIN existencias USING(tipo,modelo);
JOIN ON: Permite establecer relaciones cuya condición se establece manualmente, lo que
permite realizar asociaciones más complejas o bien asociaciones cuyos campos en las tablas
no tienen el mismo nombre:
SELECT * FROM piezas
JOIN existencias ON(piezas.tipo=existencias.tipo AND
piezas.modelo=existencias.modelo);
La última posibilidad es obtener relaciones laterales o externas (outer join). Para ello se utiliza
la sintaxis:
SELECT * FROM piezas
LEFT OUTER JOIN existencias
ON(piezas.tipo=existencias.tipo AND
piezas.modelo=existencias.modelo);
En esta consulta además de las relacionadas, aparecen las piezas no relacionadas en
existencias. Si el LEFT lo cambiamos por un RIGHT, aparecerán las existencias no presentes
en piezas.
La condición FULL OUTER JOIN produciría un resultado en el que aparecen los registros no
relacionados de ambas tablas.