DO BÁSICO AO AVANÇADO PARA MANIPULAÇÃO E OTIMIZAÇÃO DE DADOS Fábio Roberto Octaviano
Exibindo Dados de Múltiplas Tabelas
Após o término do Capítulo: Acessar dados de mais de uma tabela. Utilizar equijoins, nonequijoins. Utilizar self-joins. Produto Cartesiano de Tabelas.
EMPLOYEES DEPARTMENTS
Conforme o Padrão SQL:1999, joins incluem: Cross Joins. Natutal Joins. Cláusula USING. Full Outer Joins. Condições Arbitrárias para Outer Joins.
A cláusula NATURAL JOIN é baseada em todas as colunas de duas tabelas que possuem os mesmos nomes. Seleciona linhas de duas tabelas que têm valores iguais nas colunas em comum. Se as colunas que possuem mesmo nome forem de tipos diferentes, um erro é exibido.
SELECT department_id, department_name, location_id, city FROM departments NATURAL JOIN locations ;
SELECT department_id, department_name, location_id, city FROM departments NATURAL JOIN locations WHERE department_id IN (50, 60);
Se muitas colunas têm os mesmos nomes mas tipos de dados diferentes, o NATURAL JOIN pode ser modificado com USING para selecionar apenas as colunas desejadas. Utilize a cláusula USING para comparar apenas uma coluna ainda que mais de uma tenha o mesmo nome. NÃO use nome de tabela ou alias nas colunas referenciadas. NATURAL JOIN e USING são mutuamente exclusivos.
Exemplo de utilização de USING: SELECT l.city, d.department_name FROM locations l JOIN departments d USING (location_id) WHERE location_id = 1400; Exemplo de erro na utilização de USING: SELECT l.city, d.department_name FROM locations l JOIN departments d USING (location_id) WHERE d.location_id = 1400; ORA-25154: column part of USING clause cannot have qualifier
Normalmente envolvem Primary Key (PK) e Foreign Key (FK). Colunas relacionadas podem ser de nomes diferentes. São também chamados de EQUIJOINS ou SIMPLE JOINS.
EMPLOYEES DEPARTMENTS Foreign key Primary key
SELECT employees.employee_id, employees.last_name, departments.location_id, department_id FROM employees JOIN departments USING (department_id) ;
Utilize prefixos de tabelas para qualificar nomes de colunas que estão em múltiplas tabelas. Utilize prefixos de tabelas para distinguir entre colunas que possuem nomes idênticos e são de tabelas diferentes. NÃO utilize aliases em colunas que são identificadas na cláusula USING e listados em outras partes da instrução SQL. Use prefixos de tabelas para aumentar performance.
SELECT employees.employee_id, employees.last_name, departments.department_id, departments.location_id FROM employees JOIN departments ON employees.department_id = departments.department_id; Prefixo de tabela identifica a qual tabela a coluna department_id se refere. Quando não há colunas ambíguas, o uso de prefixo não é obrigatório, mas serve para aumentar performance pois informa ao servidor Oracle onde exatamente encontrar as colunas.
SELECT employees.employee_id, employees.last_name, departments.department_id, departments.location_id FROM employees JOIN departments ON employees.department_id = departments.department_id; Outra maneira de escrita, utilizando WHERE: SELECT employees.employee_id, employees.last_name, employees.department_id, departments.department_id, departments.location_id FROM employees, departments WHERE employees.department_id = departments.department_id;
Utilize alias em tabelas para simplificar as consultas. Utilize alias em tabelas para aumentar performance. SELECT e.employee_id, e.last_name, d.location_id, department_id FROM employees e JOIN departments d USING (department_id) ; Aliases pequenos deixam a instrução mais clara e consomem menos memória.
Natural Join é um Inner Join de todas as colunas de nomes idênticos entre duas tabelas. Utilize a cláusula ON para especificar as colunas do Join e também condições arbitrárias. A cláusula ON pode tornar o código mais claro. É separado de outros filtros utilizados no WHERE.
SELECT e.employee_id, e.last_name, e.department_id, d.department_id, d.location_id FROM employees e JOIN departments d ON (e.department_id = d.department_id);
SELECT e.employee_id, e.last_name, e.department_id, d.department_id, d.location_id FROM employees e JOIN departments d ON (e.department_id = d.department_id); Outra maneira de escrita, utilizando WHERE: SELECT e.employee_id, e.last_name, e.department_id, d.department_id, d.location_id FROM employees e, departments d WHERE e.department_id = d.department_id;
EMPLOYEES (WORKER) EMPLOYEES (MANAGER) MANAGER_ID na tabela WORKER é igual a EMPLOYEE_ID na tabela MANAGER.
SELECT e.last_name emp, m.last_name mgr FROM employees e JOIN employees m ON (e.manager_id = m.employee_id);
SELECT e.employee_id, e.last_name, e.department_id, d.department_id, d.location_id FROM employees e JOIN departments d ON (e.department_id = d.department_id) AND e.manager_id = 149 ; SELECT e.employee_id, e.last_name, e.department_id, d.department_id, d.location_id FROM employees e, departments d WHERE e.department_id = d.department_id AND e.manager_id = 149 ;
SELECT employee_id, city, department_name FROM employees e JOIN departments d ON d.department_id = e.department_id JOIN locations l ON d.location_id = l.location_id;
A condição do Join é um operador diferente de =. EMPLOYEES JOB_GRADES Salário na tabela EMPLOYEES deve estar entre o menor salário e o maior salário na tabela JOB_GRADES.
SELECT 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; No exemplo cada empregado aparece uma vez apenas devido aos ranges especificados na tabela JOB_GRADES.
SELECT 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; Outra maneira de escrita, utilizando WHERE: SELECT e.last_name, e.salary, j.grade_level FROM employees e, job_grades j WHERE e.salary BETWEEN j.lowest_sal AND j.highest_sal;
DEPARTMENTS EMPLOYEES Não há empregados no Departamento 190.
No SQL:1999, o join de duas tabelas retornando apenas linhas comuns é chamado inner join. Um join entre duas tabelas que retorna os resultados do inner join além dos registros não comuns à tabela da esquerda (ou direita) é chamado de left (ou right) outer join. Um join entre duas tabelas que retorna os resultados do inner join além dos resultados de um left e right outer join é um full outer join.
SELECT e.last_name, e.department_id, d.department_name FROM employees e LEFT OUTER JOIN departments d ON (e.department_id = d.department_id) ; SELECT e.last_name, e.department_id, d.department_name FROM employees e, departments d WHERE e.department_id = d.department_id (+) ;
SELECT e.last_name, e.department_id, d.department_name FROM employees e RIGHT OUTER JOIN departments d ON (e.department_id = d.department_id) ; SELECT e.last_name, e.department_id, d.department_name FROM employees e, departments d WHERE e.department_id (+) = d.department_id ;
SELECT e.last_name, d.department_id, d.department_name FROM employees e FULL OUTER JOIN departments d ON (e.department_id = d.department_id) ;
Um produto cartesiano é formado quando: Uma condição join é omitida. Uma condição join é inválida. Todas as linhas na primeira tabela são unidas a todas as linhas na segunda tabela. Para evitar um produto cartesiano, sempre inclua uma condição join válida.
EMPLOYEES (20 rows) DEPARTMENTS (8 rows) Produto Cartesiano: 20 x 8 = 160 rows
A cláusula CROSS JOIN produz o produto cruzado (ou cartesiano) de duas tabelas. SELECT last_name, department_name FROM employees CROSS JOIN departments ;
Objetivos: Aplicação de Inner Joins. Aplicação de Outer Joins e Self-Joins. Adicionar condições ao Join.
1. Escreva uma consulta que gere o endereço de todos os departamentos. Utilize as tabelas Locations e Countries. Mostre o location_id, endereço, cidade, estado e país. Use NATURAL JOIN para gerar os resultados. SELECT location_id, street_address, city, state_province, country_name FROM locations NATURAL JOIN countries;
2. O RH precisa de um relatório de empregados de Toronto. Mostre o sobrenome, cargo, número do departamento e nome do departamento de todos os empregados que trabalhem em Toronto. SELECT e.last_name, e.job_id, e.department_id, d.department_name FROM employees e JOIN departments d ON (e.department_id = d.department_id) JOIN locations l ON (d.location_id = l.location_id) WHERE LOWER(l.city) = 'toronto';
3. Crie um relatório para mostrar o sobrenome e número dos empregados, além do sobrenome e número de seus respectivos gerentes. Chame as colunas de Empregado, Emp#, Gerente, Ger#, respectivamente. Salve a consulta. SELECT w.last_name "Employee", w.employee_id "EMP#", m.last_name "Manager", m.employee_id "Mgr#" FROM employees w join employees m ON (w.manager_id = m.employee_id);
4. Modifique a consulta anterior para exibir todos os empregados, incluindo os que não possuem gerente. Salve a sua consulta. SELECT w.last_name "Employee", w.employee_id "EMP#", m.last_name "Manager", m.employee_id "Mgr#" FROM employees w LEFT OUTER JOIN employees m ON (w.manager_id = m.employee_id);
5. Crie um relatório que mostre os sobrenomes dos empregados, números dos departamentos e todos os empregados que trabalhem no mesmo departamento de um dado empregado. Chame essa coluna de Colega. Salve a consulta.
5. Solução: SELECT e.department_id department, e.last_name employee, c.last_name Colega FROM employees e JOIN employees c ON (e.department_id = c.department_id) WHERE e.employee_id <> c.employee_id ORDER BY e.department_id, e.last_name, c.last_name;
6. Crie um relatório que mostre o sobrenome, o cargo, o nome do departamento, salário e categoria de todos os empregados. SELECT e.last_name, e.job_id, d.department_name, e.salary, j.grade_level FROM employees e JOIN departments d ON (e.department_id = d.department_id) JOIN job_grades j ON (e.salary BETWEEN j.lowest_sal AND j.highest_sal);
7. Crie um relatório que mostre o sobrenome e a data de contratação de todos os empregados contratados posteriormente ao empregado Davies. SELECT e.last_name, e.hire_date FROM employees e JOIN employees davies ON (davies.last_name = 'Davies') WHERE davies.hire_date < e.hire_date;
8. Exiba o sobrenome e data de contratação de todos os empregados que foram contratados antes de seus gerentes, exibindo também o sobrenome e data de contratação de seus gerentes. SELECT w.last_name, w.hire_date, m.last_name, m.hire_date FROM employees w JOIN employees m ON (w.manager_id = m.employee_id) WHERE w.hire_date < m.hire_date;