Calificación de Columnas en un Join
Cuando se unen dos o más tablas , se necesita calificar los nombres de las columnas con el nombre de la tabla para evitar la ambigüedad. Sin los prefijos de tabla, por ejemplo para mostrar la identificación del departamento y su nombre, la columna DEPARTMENT_ID en la lista SELECT podría ser de la tabla DEPARTMENTS o la tabla EMPLOYEES. Es necesario entonces añadir el prefijo de tabla para ejecutar la consulta. Si no hay nombres de columna comunes entre las dos tablas , no hay necesidad de calificar las columnas pero, utilizando el prefijo de la tabla mejora el rendimiento, ya que le dice al servidor de Oracle exactamente dónde encontrar las columnas.
Sin embargo, calificar nombres de columna con los nombres de tabla puede llevar mucho tiempo, sobre todo si los nombres de tabla son largos. En su lugar, puede utilizar alias de tabla . Así como un alias de columna da una columna de otro nombre, un alias de tabla da una tabla con otro nombre. Los alias de tabla ayudan a mantener el código SQL más pequeño, por lo tanto, usando menos memoria.
El nombre de la tabla se especifica por completo, seguido por un espacio y luego el alias de la tabla.
-- Ejemplo SELECT e.employee_id "ID EMPLEADO", e.job_id "ID TRABAJO", j.job_title "DESCRIPCION TRABAJO" FROM employees e JOIN jobs j ON(e.job_id = j.job_id) ORDER BY e.employee_id;
En la sentencia del ejemplo, en la cláusula FROM a la tabla EMPLOYEES se le asigna el alias e y a la tabla JOBS se le asigna el alias j. Son estos alias los que se utilizan en las cláusulas SELECT, ON y ORDER BY para hacer referencia en forma más clara y eficiente a las columnas de cada tabla.
La sentencia entonces, retorna la identificación del empleado, identificación del trabajo y nombre del trabajo que cada empleado desempeña. La información se muestra ordenada por identificación del empleado.
Creando Joins con la cláusula NATURAL JOINS
La cláusula Natural Join establece una relación de igualdad basada en TODAS las columnas de dos tablas que poseen el mismo nombre.
Permite seleccionar filas desde dos tablas que tengan los mismos valores en todas las columnas del mismo nombre.
Si las columnas tienen el mismo nombre pero diferentes tipos de datos el servidor Oracle retorna un error.
-- Ejemplo SELECT department_id, department_name, location_id, city FROM departments NATURAL JOIN locations;
En la sentencia del ejemplo, los datos que se desean mostrar existen en las tabla DEPARTMENTS y LOCATIONS las que se relacionan a través de la columna location_id que es la única con el mismo nombre en ambas tablas. Si existieran otras columnas con el mismo nombre en ambas tablas la cláusula NATURAL JOIN también usaría esas columnas para relacionar ambas tablas.
-- Ejemplo SELECT department_id, department_name, location_id, city FROM departments NATURAL JOIN locations WHERE department_id IN(20,50);
En el ejemplo, se muestra información de las tablas DEPARTMENTS y LOCATIONS relacionadas a través de la columna location_id. Las filas que se muestran son para el departamento 20 ó 50.
Creando Joins con la cláusula USING
Si en las tablas desde las cuales se desean obtener datos existen varias columnas que tienen los mismos nombres, a través de la cláusula USING se puede especificar las columnas que se deben utilizar para una unión de igualdad.
La cláusula USING permite hacer coincidir solamente una columna cuando en ambas tablas coincidan varias columnas con el mismo nombre.
Las cláusulas NATURAL JOIN y USING son mutuamente excluyentes.
-- Ejemplo SELECT employee_id, last_name, department_id, location_id FROM employees JOIN departments USING (department_id) ORDER BY employee_id;
En la sentencia del ejemplo, las tablas EMPLOYEES y DEPARTMENTS se relacionan a través de la columna department_id para mostrar identificación del empleado, apellido, identificación del departamento y la localización del departamento al que pertenece cada empleado. La información se muestra ordenada en forma ascendente por identificación del empleado.
-- Ejemplo SELECT department_name, city FROM departments JOIN locations USING (location_id) WHERE location_id = 1400;
En la sentencia del ejemplo, las tablas DEPARTMENTS y LOCATIONS se unen a través de la columna location_id para poder mostrar el nombre del departamento y nombre de la ciudad que se encuentra en la localización 1400.
-- Sentencia incorrecta SELECT l.city, d.department_name FROM locations l JOIN departments d USING (location_id) WHERE d.location_id = 1400; -- Sentencia correcta SELECT l.city, d.department_name FROM locations l JOIN departments d USING (location_id) WHERE location_id = 1400;
Al unir tablas con la cláusula USING, no se puede calificar a una columna que se utiliza en la propia cláusula USING. Además, si esa columna se utiliza en cualquier parte de la instrucción SQL, no se puede utilizar un alias en ella. En la sentencia izquierda del ejemplo, no debe usar un alias en la columna location_id en la cláusula WHERE, porque la columna se utiliza en la cláusula USING. La sentencia derecha del ejemplo es la correcta
La columna que se hace referencia en la cláusula USING no deberían tener una calificación (nombre de tabla o alias) en cualquier parte de la instrucción SQL.
Creando Joins con la cláusula ON
La condición de unión para los NATURAL JOIN es básicamente una unión de igualdad de todas las columnas con el mismo nombre.
Para especificar las columnas por las cuales se quiere para unir dos tablas, se utiliza la cláusula ON.
La condición de JOIN o de unión se separan de otras condiciones de búsqueda (WHERE).
También se puede usar la cláusula ON para unir columnas que tengan diferentes nombres pero contienen el mismo dato en la misma tabla o en tablas diferentes.
-- Ejemplo SELECT e.employee_id, e.last_name, e.department_id "DEPTO.EMPLOYEES", d.department_id "DEPTO.DEPARTMENTS", d.location_id FROM employees e JOIN departments d ON (e.department_id = d.department_id) ORDER BY e.employee_id;
En la sentencia del ejemplo, se muestra información de las tablas EMPLOYEES y DEPARTMENTS uniéndolas a través de la columna department_id que existe en ambas. Los datos de ambas tablas se muestran cuando el valor en la columna department_id de la tabla EMPLOYEES exista en la columna department_id de la tabla DEPARTMENTS. Por esta razón se muestran 106 filas ya que el empleado que posee NULO en la identificación de departamento no se muestra.
-- Ejemplo SELECT d.department_id departamento, d.location_id localidad, l.city ciudad FROM departments d JOIN locations l ON (d.location_id = l.location_id) ORDER BY d.department_id;
En el ejemplo, se muestra información de las tablas DEPARTMENTS y LOCATIONS uniéndolas a través de la columna location_id que existe en ambas. Los datos de ambas tablas se muestran cuando el valor en la columna location_id de la tabla DEPARTMENTS exista en la columna location_id de la tabla LOCATIONS.
Creando Joins en Tres Sentidos
Un Join en tres sentidos es la unión de tres tablas y se ejecutan de izquierda a derecha. Esto quiere decir, que en el ejemplo el primer join que se ejecuta es EMPLOYEES JOIN DEPARTMENTS y posteriormente el JOIN de DEPARTMENTS con LOCATIONS los departamentos retornados por el primer JOIN,
La condición del primer join puede referenciar sólo las columnas de las tablas del primer Join (en el ejemplo de las tablas EMPLOYEES o DEPARTMENTS) pero no puede referenciar columnas de la tabla del segundo Join (en el ejemplo tabla LOCATIONS). La condición del segundo join puede referenciar columnas de las tres tablas (EMPLOYEES, DEPARTMENTS o LOCATIONS).
-- Ejemplo SELECT emp.employee_id, dep.department_name, loc.city FROM employees emp JOIN departments dep USING(department_id) JOIN locations loc USING(location_id);
En el ejemplo, se obtiene la identificación de los empleados, nombre del departamento (si la identificación del departamento existen en las tablas EMPLOYEES y DEPARTAMENTS) y la ciudad en la que se encuentra el departamento (si la localidad de los departamentos retornados en el primer join existe en la tabla LOCATIONS).
-- Ejemplo SELECT emp.employee_id, dep.department_name, loc.city FROM employees emp JOIN departments dep ON emp.department_id = dep.department_id JOIN locations loc ON dep.location_id = loc.location_id;
Agregando Condiciones Adicionales a un Join
Para agregar una condición a la cláusula USING se debe agregar la cláusula WHERE. Si se desea incorporar más condiciones, se deben agregar las cláusulas AND que se deseen a continuación a continuación de la cláusula WHERE.
-- Ejemplo SELECT employee_id, salary, e.manager_id, department_id, department_name FROM employees e JOIN departments USING(department_id) WHERE e.manager_id IN(149,100) AND salary < 10000;
En el ejemplo, se muestra la información de los empleados cuyo jefe sea 149 o 100 y además posean un salario menor a 10000
-- Ejemplo SELECT e.employee_id, e.salary, e.manager_id, e.department_id, d.department_name FROM employees e JOIN departments d ON (e.department_id = d.department_id) AND e.manager_id IN(149,100) AND salary < 10000; SELECT e.employee_id, e.salary, e.manager_id, e.department_id, d.department_name FROM employees e JOIN departments d ON(e.department_id = d.department_id) WHERE e.manager_id IN(149,100) AND salary < 10000;
El ejemplo se basa en la misma sentencia anterior, pero como ahora se utiliza la cláusula ON en el Join, para agregar condiciones se puede utilizar la cláusula AND (ejemplo de la izquierda). Opcionalmente, se puede utilizar la cláusula condicional WHERE seguido de las cláusulas AND que se deseen (ejemplo de la derecha).
Join sobre la misma tabla o Self-Join
En ocasiones se debe realizar un join sobre la misma tabla para obtener algún dato. Por lo tanto, es necesario unir las columnas de la tabla que contienen el mismo dato simulando que pertenecen a tablas diferentes.
-- Ejemplo SELECT e.employee_id "Id Empleado", e.last_name "Empleado", e.manager_id "Id Jefe", m.last_name "Jefe" FROM employees e JOIN employees m ON (e.manager_id = m.employee_id) ORDER BY e.employee_id;
En el ejemplo, para obtener el nombre del jefe de cada empleado se debe realizar un join sobre la misma tabla EMPLOYEES ó Self-Join ya que la información que almacena la columna manager_id es la misma de la columna employee_id. Para ello se unen ambas columnas de la tabla EMPLEOYEES simulando que pertenecen a tablas diferentes.
Nonequijoins
Es una condición de unión que contiene un operador de comparación que no sea el operador de igualdad (=). Por simplicidad se utiliza BETWEEN. La relación entre la tabla EMPLOYEES y la tabla JOB_GRADES es un ejemplo de un nonequijoin. La columna SALARY en la tabla EMPLOYEES oscila entre los valores de las columnas LOWEST_SAL y HIGHEST_SAL de la tabla JOB_GRADES. Por lo tanto, cada empleado puede ser clasificado en función de su salario. Otras condiciones (por ejemplo, <= y >=) se puede utilizar, pero BETWEEN es el más simple.
-- Ejemplo SELECT e.employee_id, e.last_name, e.salary, j.grade_level FROM employees e JOIN job_grades j ON e.salary BETWEEN j.lowest_sal AND j.highest_sal ORDER BY e.employee_id;
En el ejemplo, se un nonequijoin para evaluar el grado de salario de cada empleado. El salario del empleado debe estar entre cualquier par de los rangos bajo y alto de la tabla JOB_GRADES para que aparezca en la consulta.
Outer Joins o Uniones Externas
-- Ejemplo SELECT employee_id, department_id, department_name FROM employees JOIN departments USING (department_id) ORDER BY employee_id;
En un EQUIJOINS o Join de Igualdad, si una fila no satisface la condición del join no aparece en el resultado de la consulta SQL. En el ejemplo, al efectuar un Equijoin entre las tablas EMPLOYEES y DEPARTAMENTS utilizando la columna department_id, sólo se mostrarán los empleados cuyo departamento existe en la tabla DEPARTMENTS. Por lo anterior, el empleado 178 no aparece entre las filas seleccionadas, ya que el departamento Nulo no existe en la tabla DEPARTMENTS. Para que el Join retorne todos los empleados exista o no su departamento en la tabla DEPARTMENTS, se debe efectuar un OUTER JOIN o Uniones Externas.
- En la versión SQL de 1999 la combinación de dos tablas que retornan sólo filas coincidentes se llama INNER JOIN o Unión Interna.
- Un Join entre dos tablas que retorna el resultado del INNER JOIN (filas que coinciden) así como las filas de la tabla a la izquierda que no existen en tabla de la derecha del Join es una Unión
Externa Izquierda o LEFT OUTER JOIN. - Una unión entre dos tablas que retorna el resultado de un INNER JOIN así como las filas de la tabla a la derecha que no existen en tabla de la izquierda del Join es una Unión Externa
Derecha o RIGTH OUTER JOIN. - Una unión entre dos tablas que retorna el resultado de un INNER JOIN así como los de la Unión Externa izquierda y las filas de la Unión Externa Derecha es una Unión Externa Completa o
FULL OUTER JOIN.
Left Outer Join
-- Ejemplo SELECT emp.employee_id, dep.department_id, dep.department_name FROM employees emp LEFT OUTER JOIN departments dep ON(emp.department_id = dep.department_id) ORDER BY emp.employee_id;
En la sentencia del ejemplo, para poder mostrar todos los empleados de la tabla EMPLOYEES (a la izquierda del Join) exista o no su departamento en la tabla DEPARTMENTS se debe utilizar LEFT OUTER JOIN, de esta forma el empleado con departamento NULO aparece en el resultado final del Join. Si este Join se quisiera construir utilizando cláusula USING sería: SELECT employee_id, department_id, department_name FROM employees LEFT OUTER JOIN departments USING(department_id) ORDER BY employee_id;
Right Outer Join
-- Ejemplo SELECT emp.employee_id, dep.department_id, dep.department_name FROM employees emp RIGHT OUTER JOIN departments dep ON(emp.department_id = dep.department_id) ORDER BY emp.employee_id;
En la sentencia del ejemplo, para poder mostrar todos los departamentos de la tabla DEPARTMENTS (a la derecha del Join) exista o no el departamento en la tabla EMPLOYEES se debe utilizar RIGHT OUTER JOIN, por esta razón el empleado 178 (con departamento NULO) no aparece en el resultado final del Join pero si aparecen los departamentos todos los departamentos almacenados en la tabla DEPARTMENTS tengan o o no empleados asignados. Si este Join se quisiera construir utilizando cláusula USING sería: SELECT employee_id, department_id, department_name FROM employees RIGHT OUTER JOIN departments USING(department_id) ORDER BY employee_id;
Full Outer Join
-- Ejemplo SELECT emp.employee_id, dep.department_id, dep.department_name FROM employees emp FULL OUTER JOIN departments dep ON(emp.department_id = dep.department_id) ORDER BY emp.employee_id;
En el ejemplo, para poder mostrar todas las filas de ambas tablas existan o no coincidencias se debe usar FULL OUTER JOIN. De esta forma, la sentencia recupera todas las filas de la tabla EMPLOYEES, incluso si no hay ninguna coincidencia en la tabla DEPARTMENTS. También recupera todas las filas de la tabla de DEPARTMENTS, incluso si no hay ninguna coincidencia en la tabla EMPLOYEES. Si este Join se quisiera construir utilizando cláusula USING sería: SELECT employee_id, department_id, department_name FROM employees FULL OUTER JOIN departments USING(department_id) ORDER BY employee_id;
Guia:
/* 1.- La empresa Norteamericana "ACME" lo ha contratado a Ud. para desarrollar su nuevo Sistema de Personal. Como primera etapa se requiere que, a partir del Modelo que se presenta, pueda satisfacer los requerimientos de información que se solicitan a continuación: a) Se desea contar con un reporte actualizado del departamento en el que trabaja cada empleado. Para ello, debe mostrar el nombre completo del empleado y el nombre del departamento en el que trabaja. La información se debe mostrar como en el ejemplo: */ SELECT 'El empleado ' || e.FIRST_NAME || ' ' || e.LAST_NAME || ' trabaja en el departamento ' || d.DEPARTMENT_NAME FROM EMPLOYEES e JOIN DEPARTMENTS d USING(DEPARTMENT_ID); /* b) A los empleados cuyo salario es menor a los $2700 se les asignará un bono especial de acuerdo al trabajo que desempeñen. Para ello se requiere saber la identificación del empleado, su salario actual, identificación del trabajo y nombre del trabajo que desempeña. La información se debe mostrar para todos los empleados que se cumplan con la condición según el formato del ejemplo y ordenada en forma ascendente por salario: */ SELECT e.EMPLOYEE_ID empleado, e.SALARY salario, JOB_ID "ID TRABAJO", j.JOB_TITLE "DESCRIPCION TRABAJO" FROM EMPLOYEES e JOIN JOBS j USING(JOB_ID) WHERE e.SALARY < 2700 ORDER BY e.SALARY; /* c) Por cada departamento que posee empleados, se requiere saber el nombre del departamento y el total de empleados que trabajan en cada uno de ellos. La información se debe mostrar como se visualiza en el ejemplo y en orden alfabético por nombre de departamento: */ SELECT d.DEPARTMENT_NAME departamento, COUNT(e.EMPLOYEE_ID) "TOTAL EMPLEADOS" FROM DEPARTMENTS d JOIN EMPLOYEES e USING(DEPARTMENT_ID) GROUP BY d.DEPARTMENT_NAME ORDER BY d.DEPARTMENT_NAME; /* d) Por cada departamento en los que trabajan empleados se requiere saber el nombre del departamento y el salario máximo que se paga en cada uno de ellos. Se deben considerar sólo aquellos departamentos cuyo salario máximo sea mayor o igual a 6000 y menor o igual a 20000. La información se debe mostrar como se visualiza en el ejemplo y ordenada en forma descendente por salario máximo: */ SELECT d.DEPARTMENT_NAME departamento, MAX(e.SALARY) "SALARIO MAXIMO" FROM DEPARTMENTS d JOIN EMPLOYEES e USING(DEPARTMENT_ID) GROUP BY d.DEPARTMENT_NAME HAVING MAX(e.SALARY) BETWEEN 6000 AND 20000 ORDER BY "SALARIO MAXIMO" DESC; /* e) La Gerencia desea premiar a los empleados que han optado por trabajar en la ciudad de Toronto. Para ello, el gerente desea saber el nombre completo del empleado, el trabajo que desempeña y el departamento en el que trabaja. La información se debe mostrar en el formato del ejemplo: */ SELECT e.FIRST_NAME || ' ' || e.LAST_NAME "NOMBRE EMPLEADO", j.JOB_TITLE trabajo, d.DEPARTMENT_NAME departamento FROM DEPARTMENTS d JOIN EMPLOYEES e USING(DEPARTMENT_ID) JOIN JOBS j USING(JOB_ID); /* f) Se efectuará una redistribución del personal que trabaja en la empresa con el objetivo de potenciar los departamentos que en la actualidad NO poseen empleados trabajando en ellos. Para ello, se desea saber qué departamentos se encuentran en esta situación. La información se debe visualizar como se muestra en el ejemplo y ordenada en forma ascendente por identificación del departamento: */ SELECT d.DEPARTMENT_ID "ID DEPARTAMENTO", d.DEPARTMENT_NAME "NOMBRE DEPARTAMENTO", COUNT(e.DEPARTMENT_ID) "TOTAL EMPLEADOS" FROM DEPARTMENTS d LEFT OUTER JOIN EMPLOYEES e ON (d.DEPARTMENT_ID = e.DEPARTMENT_ID) GROUP BY d.DEPARTMENT_ID ,d.DEPARTMENT_NAME HAVING COUNT(e.DEPARTMENT_ID) = 0 ORDER BY d.DEPARTMENT_ID; /* 2 a) Por motivos de salud, los dueños de la empresa se ausentarán del país por 6 meses aproximadamente. Por esta razón han dejado a cargo del negocio a una persona de su confianza la que necesita saber cuál es el trabajo que desempeña cada empleado. Para ello, la información que se requiere es el nombre y apellido del empleado (concatenado) y el nombre del trabajo que desempeña en el formato que se muestra en el ejemplo y ordenada en forma ascendente por apellido: */ SELECT 'El empleado ' || PNOMBRE || ' ' || APPATERNO || ' se desempeña como ' || DESC_TIPO_EMPLEADO FROM EMPLEADO e NATURAL JOIN TIPO_EMPLEADO te ORDER BY APPATERNO; /* 2 b) El nuevo encargado de la empresa desea saber todas las ventas que se han efectuado a la fecha. Para ello saber el nombre y apellido del empleado (concatenado) que efectuó la venta, el número de la boleta, fecha de la boleta y el monto total de la boleta. La información se debe mostrar según el formato del ejemplo y ordenada por número de boleta en forma ascendente:*/ SELECT PNOMBRE || ' ' || APPATERNO "NOMBRE EMPLEADO", NRO_BOLETA "NUMERO BOLETA", FECHA_BOLETA "FECHA BOLETA", TO_CHAR(MONTO_TOTAL,'$999,999') "MONTO VENTA" FROM EMPLEADO e NATURAL JOIN VENTAS v ORDER BY NRO_BOLETA; /* 2 c) Se ha detectado que en el mes de Marzo los cálculos de comisiones por venta se efectuaron en forma errónea, calculándose menos del valor que les corresponde. Por esta razón, se debe enviar a contabilidad un reporte de las comisiones que se pagaron durante el mes de Marzo del 2014. La información que se requiere es el nombre y apellido del empleado (concatenados), número de la boleta, monto total de la boleta y el valor de la comisión asociado a la boleta. La información se debe mostrar en el formato que se presenta en el ejemplo y ordena en forma ascendente por apellido y monto total de la boleta: */ SELECT PNOMBRE || ' ' || APPATERNO "NOMBRE EMPLEADO", NRO_BOLETA "NUMERO BOLETA", TO_CHAR(MONTO_TOTAL,'$999,999') "MONTO BOLETA", TO_CHAR(MONTO_COMISION,'$99,999') "COMISION POR VENTA" FROM EMPLEADO e NATURAL JOIN VENTAS v NATURAL JOIN COMISION_VENTAS cv WHERE FECHA_BOLETA >= '01032014' AND FECHA_BOLETA <= '31032014' ORDER BY APPATERNO, MONTO_TOTAL; /* 2 d) Se desea saber cuál es el monto total de las ventas y comisiones de los vendedores que efectuaron alguna venta en el mes de Marzo del 2014. La información que se requiere es el nombre y apellido del empleado (concatenados), monto total de sus ventas y monto de sus comisiones de acuerdo al formato que se muestra en el ejemplo y ordenada por el total de comisión del mes: */ SELECT PNOMBRE || ' ' || APPATERNO "NOMBRE EMPLEADO", TO_CHAR(SUM(MONTO_TOTAL),'$999,999') "TOTAL VENTAS DEL MES", TO_CHAR(SUM(MONTO_COMISION),'$99,999') "TOTAL COMISION DEL MES" FROM EMPLEADO e NATURAL JOIN VENTAS v NATURAL JOIN COMISION_VENTAS cv WHERE FECHA_BOLETA >= '01032014' AND FECHA_BOLETA <= '31032014' GROUP BY PNOMBRE, APPATERNO ORDER BY SUM(MONTO_COMISION); /* 2 e) Con el objetivo de garantizar una atención de excelencia a sus clientes, la empresa ha decido que es imprescindible renovar su planta de vendedores. Para ello, una de las consideraciones para decidir los vendedores serán reemplazados tiene relación con el total de ventas que han efectuado hasta la fecha (cuántas). Para ello, se deben considerar a TODOS lo vendedores hayan efectuado o no ventas ya que los primeros candidatos a ser reemplazados son los que hasta le fecha no han efectuado ninguna venta. La información que se requiere es el nombre y apellido del empleado (concatenados) y el total de ventas efectuadas hasta la fecha según el formato que se muestra en el ejemplo y ordenada por apellido: */ SELECT e.PNOMBRE || ' ' || e.APPATERNO "NOMBRE EMPLEADO", NVL(COUNT(v.NRO_BOLETA),0) "TOTAL VENTAS" FROM EMPLEADO e LEFT OUTER JOIN VENTAS v ON(e.ID_EMPLEADO = v.ID_EMPLEADO) GROUP BY e.PNOMBRE, e.APPATERNO ORDER BY e.APPATERNO; /* 2 f) A fin de mes se les deberá notificar el despido a los empleados que hasta la fecha no han efectuado ninguna venta. Debido a esto, se requiere tener un listado con los nombres de aquellos empleados que están en esa condición. La información que se requiere es el nombre del empleado (nombre y apellido) y la fecha en la cuál fue contratado y se debe mostrar en el formato del ejemplo: */ SELECT 'El empleado ' || e.PNOMBRE || ' ' || e.APPATERNO || ' contratado el ' || e.FECHA_CONTRATO || ' no ha efectuado ventas hasta la fecha' "PERSONAL SIN VENTAS" FROM EMPLEADO e LEFT OUTER JOIN VENTAS v ON(e.ID_EMPLEADO = v.ID_EMPLEADO) GROUP BY e.PNOMBRE, e.APPATERNO, e.FECHA_CONTRATO HAVING COUNT(v.NRO_BOLETA) = 0;
No hay comentarios:
Publicar un comentario