comandos sql e exercicios avançados

56
Junção de Tabelas A figura abaixo resume as operações de junção: As operações de junção dividem-se em dois grupos: horizontais e verticais. As horizontais actuam sobre linhas enquanto as verticais actuam sobre colunas. Junção horizontal Utiliza-se uma junção horizontal quando a consulta SQL requer dados de duas ou mais tabelas. Esta é a operação que na prática tira partido do conceito "base de dados relacional" pois permite mostrar os dados que estão armazenados em diferentes tabelas como se estivessem armazenados numa única, desde que essas tabelas possuam um relacionamento entre si. A tabela resultado é construída a partir de uma das tabelas originais, acrescentando colunas da segunda tabela, o que corresponde a um crescimento horizontal. A norma SQL99 define a sintaxe usada pelas cláusulas do comando SELECT para fazer os diferentes tipos de junção. A base de dados Oracle suporta esta sintaxe desde a versão 9.0. Nas versões anteriores à 9.0 era usada uma sintaxe antiga, que no caso da junção externa possuia uma notação proprietária. Por razões de compatibilidade com o passado a sintaxe antiga com as extensões

Upload: joel-campos

Post on 08-Aug-2015

486 views

Category:

Documents


2 download

DESCRIPTION

dddddd

TRANSCRIPT

Page 1: Comandos sql e exercicios avançados

Junção de Tabelas

A figura abaixo resume as operações de junção:

As operações de junção dividem-se em dois grupos: horizontais e verticais.

As horizontais actuam sobre linhas enquanto as verticais actuam

sobre colunas.

Junção horizontal

Utiliza-se uma junção horizontal quando a consulta SQL requer dados de duas

ou mais tabelas. Esta é a operação que na prática tira partido do conceito "base

de dados relacional" pois permite mostrar os dados que estão armazenados em

diferentes tabelas como se estivessem armazenados numa única, desde que

essas tabelas possuam um relacionamento entre si. A tabela resultado é

construída a partir de uma das tabelas originais, acrescentando colunas da

segunda tabela, o que corresponde a um crescimento horizontal.

A norma SQL99 define a sintaxe usada pelas cláusulas do comando SELECT

para fazer os diferentes tipos de junção. A base de dados Oracle suporta esta

sintaxe desde a versão 9.0. Nas versões anteriores à 9.0 era usada uma sintaxe

antiga, que no caso da junção externa possuia uma notação proprietária. Por

razões de compatibilidade com o passado a sintaxe antiga com as extensões

Page 2: Comandos sql e exercicios avançados

proprietárias contínua disponível. Neste manual os comandos serão dados

seguindo as duas abordagens: sintaxe antiga e SQL99.

Produto cartesiano

O produto cartesiano entre dois conjuntos é um terceiro conjunto constituído

por todos os elementos do primeiro combinados com todos os elementos do

segundo.

Os comandos abaixo geram o produto cartesiado entre as tabelas EMP e DEP:

Sintaxe antiga SQL99

SELECT emp.empno,

emp.ename,

emp.deptno,

dept.deptno,

dept.dname

FROM emp, dept;

SELECT emp.empno,

emp.ename,

emp.deptno,

dept.deptno,

dept.dname

FROM emp CROSS JOIN dept;

EMPNO ENAME DEPTNO DEPTNO

DNAME

---------------------- ---------- ---------------------- -------------

--------- --------------

7369 SMITH 20 10

ACCOUNTING

7499 ALLEN 30 10

ACCOUNTING

7521 WARD 30 10

ACCOUNTING

7566 JONES 20 10

ACCOUNTING

7654 MARTIN 30 10

ACCOUNTING

7698 BLAKE 30 10

ACCOUNTING

7782 CLARK 10 10

ACCOUNTING

7788 SCOTT 20 10

ACCOUNTING

7839 KING 10 10

ACCOUNTING

7844 TURNER 30 10

ACCOUNTING

7876 ADAMS 20 10

ACCOUNTING

7900 JAMES 30 10

ACCOUNTING

7902 FORD 20 10

ACCOUNTING

7934 MILLER 10 10

ACCOUNTING

7369 SMITH 20 20

RESEARCH

Page 3: Comandos sql e exercicios avançados

7499 ALLEN 30 20

RESEARCH

7521 WARD 30 20

RESEARCH

7566 JONES 20 20

RESEARCH

7654 MARTIN 30 20

RESEARCH

7698 BLAKE 30 20

RESEARCH

7782 CLARK 10 20

RESEARCH

7788 SCOTT 20 20

RESEARCH

7839 KING 10 20

RESEARCH

7844 TURNER 30 20

RESEARCH

7876 ADAMS 20 20

RESEARCH

7900 JAMES 30 20

RESEARCH

7902 FORD 20 20

RESEARCH

7934 MILLER 10 20

RESEARCH

7369 SMITH 20 30

SALES

7499 ALLEN 30 30

SALES

7521 WARD 30 30

SALES

7566 JONES 20 30

SALES

7654 MARTIN 30 30

SALES

7698 BLAKE 30 30

SALES

7782 CLARK 10 30

SALES

7788 SCOTT 20 30

SALES

7839 KING 10 30

SALES

7844 TURNER 30 30

SALES

7876 ADAMS 20 30

SALES

7900 JAMES 30 30

SALES

7902 FORD 20 30

SALES

7934 MILLER 10 30

SALES

7369 SMITH 20 40

OPERATIONS

7499 ALLEN 30 40

OPERATIONS

7521 WARD 30 40

OPERATIONS

7566 JONES 20 40

Page 4: Comandos sql e exercicios avançados

OPERATIONS

7654 MARTIN 30 40

OPERATIONS

7698 BLAKE 30 40

OPERATIONS

7782 CLARK 10 40

OPERATIONS

7788 SCOTT 20 40

OPERATIONS

7839 KING 10 40

OPERATIONS

7844 TURNER 30 40

OPERATIONS

7876 ADAMS 20 40

OPERATIONS

7900 JAMES 30 40

OPERATIONS

7902 FORD 20 40

OPERATIONS

7934 MILLER 10 40

OPERATIONS

56 rows selected

Na sintaxe proprietária o produto cartesiano é obtido quando se faz

referencia a mais que duas tabelas e não se coloca uma condição de

JOIN na cláusula WHERE;

Na sintaxe SQL99 o produto cartesiano é obtido usando a cláusula

CROSS JOIN;

A sintaxe SQL99 é menos susceptível a erros, pois é preciso usar

explicitamente a cláusula CROSS JOIN, enquanto que na sintaxe

proprietária, se houver esquecimento da cláusula de JOIN ou erro na

sua construção, sai um produto cartesiano;

O resultado do produto cartesiano raramente é útil, pois mostra

combinações de linhas que não têm relação entre si e por isso não são

úteis para o utilizador. A sua execução causa muito I/O na base de

dados;

Junção Interna

Sumário

Page 5: Comandos sql e exercicios avançados

A junção interna é uma operação de junção horizontal entre duas tabelas, que

usa uma comparação por igualdade entre a(s) coluna(s) comum(ns).

Normalmente a(s) coluna(s) comum(ns) é(são) Foreign Key numa tabela e

Primary Key ou Unique Key na outra. A junção interna pode ser vista como

um produto cartesiano filtrado, pois exige que as linhas da tabela da esquerda

tenham correspondente na tabela da direita, sendo o valor da coluna comum

igual. O diagrama apresentado a seguir mostra como funciona a junção interna

entre duas tabelas:

A junção interna é a operação mais importante nas bases de dados relacionais,

pelo que é suportada desde sempre. A norma SQL99 propos uma nova sintaxe

para esta operação, tendo disponibilizado várias cláusulas que serão analisadas

nos exemplos abaixo, onde também faremos a comparação com a sintaxe

antiga. Os temas abordados serão:

Cláusula ON

Cláusula USING

Cláusula NATURAL JOIN

Comparação entre ON, USING e NATURAL JOIN

Sinónimos para nomes de tabelas

Junção com mais de duas tabelas

A cláusula de junção e a cláusula de filtro

A junção feita com coluna(s) UNIQUE

Cláusula ON

As tabelas EMP e DEPT possuem uma relação entre si, implementada através

da coluna comum DEPTNO. Na tabela EMP sabemos qual o número do

departamento em que o empregado trabalha. Na tabela DEPT sabemos o

número, nome e localização desse departamento. Para juntar os dois conjuntos

efectuamos uma JUNÇÃO horizontaldas duas tabelas, usando uma igualdade

de valores na coluna comum, como ilustrado nos exemplos abaixo:

Sintaxe antiga SQL99

SELECT emp.empno, SELECT emp.empno,

Page 6: Comandos sql e exercicios avançados

emp.ename,

emp.deptno,

dept.deptno,

dept.dname,

dept.loc

FROM emp, dept

WHERE emp.deptno=dept.deptno;

emp.ename,

emp.deptno,

dept.deptno,

dept.dname,

dept.loc

FROM emp

INNER JOIN dept ON

(emp.deptno=dept.deptno);

EMPNO ENAME DEPTNO DEPTNO

DNAME LOC

---------------------- ---------- ---------------------- -------------

--------- -------------- -------------

7369 SMITH 20 20

RESEARCH DALLAS

7499 ALLEN 30 30

SALES CHICAGO

7521 WARD 30 30

SALES CHICAGO

7566 JONES 20 20

RESEARCH DALLAS

7654 MARTIN 30 30

SALES CHICAGO

7698 BLAKE 30 30

SALES CHICAGO

7782 CLARK 10 10

ACCOUNTING NEW YORK

7788 SCOTT 20 20

RESEARCH DALLAS

7839 KING 10 10

ACCOUNTING NEW YORK

7844 TURNER 30 30

SALES CHICAGO

7876 ADAMS 20 20

RESEARCH DALLAS

7900 JAMES 30 30

SALES CHICAGO

7902 FORD 20 20

RESEARCH DALLAS

7934 MILLER 10 10

ACCOUNTING NEW YORK

14 rows selected

Com esta operação o utilizador consegue visualizar o nome do

empregado, o nome do departamento em que trabalha e a sua

localização, ou seja, vê os dados de duas tabelas relacionadas como se

fossem uma única;

Esta junção pode ser interpretada como um produto cartesiano ao qual

foram eliminadas as linhas que não satisfazem a condição de junção;

O comentário anterior mostra como interpretar a junção em

comparação com o produto cartesiano, mas não revela a forma como o

Page 7: Comandos sql e exercicios avançados

motor da base de dados efectivamente resolve a operação. Esta

operação é tão importante que os construtores de motores relacionais

investem muitos recursos para optimizar o desempenho, o que resulta

na existência de vários meios para executar a junção, que dependem da

dimensão das tabelas envolvidas, da existência de índices e da

selectividade das colunas comuns. Produzir um produto cartesiano e

depois filtrar linhas é um meio muito dispendioso em termos de I/O e

processamento, pelo que os motores recorrem a outros caminhos, cuja

explicação mais detalhada está fora do âmbito deste curso;

As tabelas DEPT e EMP têm em comum a coluna DEPTNO pelo que

nos comandos houve necessidade de distinguir de qual das tabelas

queremos obter a coluna. Sempre que há ambiguidade nos nomes das

colunas o utilizador é obrigado a indicar o nome da tabela a que

pertence a coluna pretendida. Por exemploEMP.DEPTNO

e DEPT.DEPTNO;

A maioria das operações de junção são internas (INNER) pelo que a

palavra reservada INNER é facultativa;

Topo

Cláusula USING

A cláusula USING está disponível na sintaxe SQL99 e pode ser usada em vez

da cláusula ON sempre que a(s) coluna(s) usada(s) na junção tenha(m) o

mesmo nome em ambas as tabelas. Esta cláusula pode ser usada mesmo que

existam outras colunas com o mesmo nome em ambas as tabelas.

No caso das tabelas EMP e DEPT a junção pode ser feita com USING:

Sintaxe antiga SQL99

SELECT emp.empno,

emp.ename,

emp.deptno,

dept.deptno,

dept.dname,

dept.loc

FROM emp, dept

WHERE emp.deptno=dept.deptno;

SELECT emp.empno,

emp.ename,

deptno,

dept.dname,

dept.loc

FROM emp

INNER JOIN dept USING (deptno);

Page 8: Comandos sql e exercicios avançados

Esta cláusula facilita a escrita do query, mas requer a validação prévia

de que a(s) coluna(s) usada(s) na junção tem(êm) o mesmo nome em

ambas as tabelas;

A cláusula USING obriga a que a(s) coluna(s) usada(s) na junção

seja(m) referenciada(s) sem o nome da tabela a que pertence(m);

Topo

Cláusula NATURAL JOIN

A cláusula NATURAL JOIN está disponível na sintaxe SQL99 e pode ser

usada em vez da cláusula ON ou em vez da cláusula USING sempre que:

A(s) coluna(s) usada(s) na junção tenha(m) o mesmo nome em ambas

as tabelas;

A(s) coluna(s) usada(s) na junção é(são) a(s) única(s) com o mesmo

nome em ambas as tabelas;

No caso das tabelas EMP e DEPT a junção pode ser feita com NATURAL

JOIN:

Sintaxe antiga SQL99

SELECT emp.empno,

emp.ename,

emp.deptno,

dept.deptno,

dept.dname,

dept.loc

FROM emp, dept

WHERE emp.deptno=dept.deptno;

SELECT emp.empno,

emp.ename,

deptno,

dept.dname,

dept.loc

FROM emp

NATURAL JOIN dept;

Esta cláusula facilita a escrita do query pois o utilizador não tem que

referir qual(ais) a(s) coluna(s) que vai usar na junção;

Esta cláusula requer a validação prévia não só de que a(s) coluna(s)

usada(s) na junção tem(êm) o mesmo nome em ambas as tabelas mas

também que é(são) a(s) única(s) coluna(s) comum(ns);

A cláusula NATURAL JOIN obriga a que a(s) coluna(s) usada(s) na

junção seja(m) referenciada(s) sem o nome da tabela a que pertence(m);

Topo

Page 9: Comandos sql e exercicios avançados

Comparação entre ON, USING e NATURAL JOIN

Consideremos as seguintes situações:

Sit

uaç

ão

Modelo entidade relacionamento Sintaxe

antiga ON

USIN

G

NAT

URA

L

JOI

N

1

SELECT *

FROM t1,

t2

WHERE

t1.t1_c1

=t2.t1_c

1;

SELECT *

FROM t1

INNER

JOIN t2

ON

(t1.t1_c

1=t2.t1_

c1);

SELECT

*

FROM

t1

INNER

JOIN

t2

USING

(t1_c1

);

SELE

CT *

FROM

t1

NATU

RAL

JOIN

t2;

2

SELECT *

FROM t3,

t4

WHERE

t3.t3_c1

=t4.t3_c

1

AND

t3.t3_c2

=t4.t3_c

2;

SELECT *

FROM t3

INNER

JOIN t4

ON

(t3.t3_c

1=t4.t3_

c1

AND

t3.t3_c2

=t4.t3_c

2);

SELECT

*

FROM

t3

INNER

JOIN

t4

USING

(t3_c1

,t3_c2

);

SELE

CT *

FROM

t3

NATU

RAL

JOIN

t4;

3

SELECT *

FROM t5,

t6

WHERE

t5.t5_c1

=t6.t6_c

2;

SELECT *

FROM t5

INNER

JOIN t6

ON

(t5.t5_c

1=t6.t6_

c2);

Não é

possív

el

Não

é

possí

vel

4

SELECT *

FROM t7,

t8

WHERE

t7.t7_c1

=t8.t7_c

1;

SELECT *

FROM t7

INNER

JOIN t8

ON

(t7.t7_c

1=t8.t7_

c1);

SELECT

*

FROM

t7

INNER

JOIN

t8

USING

(t7_c1

);

Não

é

possí

vel

Page 10: Comandos sql e exercicios avançados

Topo

Sinónimos para nomes de tabelas

O nome da tabela pode ser substituído por um sinónimo, como mostram os

exemplos abaixo:

Sintaxe antiga SQL99

SELECT e.empno,

e.ename,

e.deptno,

d.deptno,

d.dname,

d.loc

FROM emp e, dept d

WHERE e.deptno=d.deptno;

SELECT e.empno,

e.ename,

e.deptno,

d.deptno,

d.dname,

d.loc

FROM emp e

INNER JOIN dept d ON

(e.deptno=d.deptno);

A utilização de sinónimos para os nomes das tabelas apresenta as

seguintes vantagens:

Ajuda a distinguir as colunas que têm o mesmo nome nas duas tabelas.

O mesmo fim pode ser atingido usando o nome da tabela;

O query é mais rapidamente interpretado pela base de dados, pois esta

não tem duvidas de onde tem que ir buscar as colunas, pelo que poupa

processamento. Este fim também pode ser atingido recorrendo ao nome

da tabela;

Permite efectuar a junção de uma tabela com ela própria;

Topo

Junção com mais de duas tabelas

A figura abaixo mostra as relações existentes entre as tabelas EMPLOYEES,

DEPARTMENTS e LOCATIONS, pertencentes ao conjunto HR:

Page 11: Comandos sql e exercicios avançados

Este relacionamento possibilita a junção entre as 3 tabelas exemplificada pelos

comandos abaixo:

Sintaxe antiga ON USING NATURA

L JOIN

SELECT

e.first_name,

e.last_name,

d.department_id,

d.department_name,

l.city,

l.state_province

FROM employees e,

departments d,

locations l

WHERE

e.department_id=d.dep

artment_id

AND

d.location_id=l.locat

ion_id;

SELECT

e.first_name,

e.last_name,

d.department_id,

d.department_name,

l.city,

l.state_province

FROM employees e

INNER JOIN departments

d ON

(e.department_id=d.depa

rtment_id)

INNER JOIN locations l

ON

(d.location_id=l.locati

on_id);

SELECT

e.first_name

,

e.last_name,

department_i

d,

d.department

_name,

l.city,

l.state_prov

ince

FROM

employees e

INNER JOIN

departments

d USING

(department_

id)

INNER JOIN

locations l

USING

(location_id

);

Não é

possível

porque a

coluna

MANAGE

R_ID é

comúm a

Employees

e a

Departmen

ts e nao é

usada na

cláusula de

junção

deste

relacionam

ento

Page 12: Comandos sql e exercicios avançados

Na maior parte das situações é válido o seguinte principio: se há n

tabelas temos que usar n-1 condições de junção. Esta regra não é válida

quando existe mais que uma relação entre duas tabelas, como no nosso

exemplo, casos onde se deve aplicar o princípio abaixo;

Cada relacionamento entre duas tabelas exige uma condição de junção.

Neste exemplo consideramos o único relacionamento entre

LOCATIONS e DEPARTMENTS e nos vários relacionamentos

existentes entre EMPLOYEES e DEPARTMENTS consideramos

apenas EMP_DEPT_FK;

A condição de junção deve incluir todas as colunas usadas na junção;

Topo

A cláusula de junção e a cláusula de filtro

O facto de efectuarmos uma junção entre duas tabelas não impede que se faça

um filtro das linhas. O exemplo abaixo mostra como:

Sintaxe antiga ON USING NATURA

L JOIN

SELECT

e.first_name,

e.last_name,

d.department_id,

d.department_name

FROM employees e,

departments d

WHERE

e.department_id=d.depa

rtment_id

AND e.first_name like

'Jam%';

SELECT

e.first_name,

e.last_name,

d.department_id,

d.department_name

FROM employees e

INNER JOIN departments

d ON

(e.department_id=d.depa

rtment_id)

WHERE e.first_name like

'Jam%';

SELECT

e.first_name

,

e.last_name,

department_i

d,

d.department

_name

FROM

employees e

INNER JOIN

departments

d USING

(department_

id)

WHERE

e.first_name

like '%me%';

Não é

possível

porque a

coluna

MANAGE

R_ID é

comúm a

Employees

e a

Department

s e nao é

usada na

cláusula de

junção

deste

relacionam

ento

Page 13: Comandos sql e exercicios avançados

Na sintaxe antiga as condições de junção e de filtro misturam-se, sendo

necessária uma leitura mais atenta para as distinguir;

Na sintaxe SQL99 há uma separação clara entre condições de junção e

de filtro;

Topo

A junção feita com colunas UNIQUE

As boas práticas recomendam que a junção entre duas tabelas seja feita entre a

chave primária (PK - Primary Key) de uma e a chave estrangeira (FK -

Foreign Key) da outra tabela. O motor relacional do Oracle permite que a

chave primária seja substituída por uma chave alternativa. Por exemplo, em

vez do número de empregado poderíamos usar o NIF (Número de

Identificação Fiscal), desde que este seja único para cada empregado e tenha

um valor não nulo. Para isto a base de dados requer a existência de um índice

com a propriedade UNIQUE ou que seja definida a restrição UNIQUE sobre

a(s) coluna(s) envolvida(s).

Junção externa

O diagrama entidade relacionamento a seguir apresentado mostra os

relacionamentos existentes entre as tabelas EMPLOYEES e

DEPARTMENTS. Considerando apenas o relacionamento "Trabalha Em"

verificamos que um empregado pode não ter um departamento atribuído,

assim como um departamento pode não ter empregados atribuídos. Os

empregados que não têm departamento e os departamentos que não têm

empregado não aparecem no resultado de uma junção interna, mas aparecem

na junção externa. A junção externa é uma extensão da junção interna.

Page 14: Comandos sql e exercicios avançados

Junção externa à esquerda

Junção externa à direita

Junção externa FULL (à esquerda e à direita)

Junção externa à esquerda

A junção externa à esquerda estende o resultado da junção interna, pois

mostra todas as linhas que são devolvidas pela junção interna e acrescenta as

linhas da tabela da esquerda que não têm correspondência na tabela da direita.

A junção envolve sempre duas tabelas, sendo a da esquerda a que primeiro

aparece no comando. O diagrama apresentado a seguir mostra como funciona

este tipo de junção:

Os comandos apresentados a seguir mostram como fazer a junção externa à

esquerda entre as tabelas EMPLOYEES e DEPARTMENTS. Esta operação

vai mostrar as linhas obtidas pela junção interna, acrescida dos empregados

que não têm um departamento atribuído, o que é possível porque a coluna

DEPARTMENT_ID em EMPLOYEES suporta valores nulos:

Sintaxe proprietária ON USING

NATUR

AL

JOIN

Page 15: Comandos sql e exercicios avançados

SELECT

e.first_name,

e.last_name,

d.department_id,

d.department_name

FROM employees e,

departments d

WHERE

e.department_id=d.depa

rtment_id (+);

SELECT

e.first_name,

e.last_name,

d.department_id,

d.department_name

FROM employees e

LEFT OUTER JOIN

departments d ON

(e.department_id=d.depart

ment_id);

SELECT

e.first_name

,

e.last_name,

department_i

d,

d.department

_name

FROM

employees e

LEFT OUTER

JOIN

departments

d USING

(department_

id);

Não

aplicável

Topo

Junção externa à direita

A junção externa à direita funciona da mesma forma que a junção externa à

esquerda, mas mostra as linhas da tabela da direita que não têm

correspondência com linhas da tabela da esquerda. Este resultado pode ser

obtido por uma junção à esquerda trocando a ordem das colunas. O diagrama

apresentado a seguir descreve o funcionamento deste tipo de junção:

Os comandos apresentados a seguir mostram como fazer a junção externa à

direita entre as tabelas EMPLOYEES e DEPARTMENTS. Esta operação vai

mostrar as linhas obtidas pela junção interna, acrescida dos departamentos que

não têm um empregado atribuído:

Sintaxe

proprietária ON USING

NATURA

L JOIN

SELECT

SELECT

e.first_name,

SELECT

Não

Page 16: Comandos sql e exercicios avançados

e.first_name,

e.last_name,

d.department_i

d,

d.department_n

ame

FROM employees

e, departments

d

WHERE

e.department_i

d (+)

=d.department_

id;

e.last_name,

d.department_id,

d.department_name

FROM employees e

RIGHT OUTER JOIN departments

d ON

(e.department_id=d.department

_id);

e.first_name,

e.last_name,

department_id,

d.department_n

ame

FROM employees

e

RIGHT OUTER

JOIN

departments d

USING

(department_id

);

aplicável

Topo

Junção externa FULL (à esquerda e à direita)

A junção externa FULL mostra a soma das junções externas à esquerda e à

direita, como exemplificado no próximo diagrama:

Os comandos apresentados a seguir mostram como fazer a junção externa

FULL entre as tabelas EMPLOYEES e DEPARTMENTS:

Sintaxe

proprietária ON USING

NATURA

L JOIN

SELECT

e.first_name,

e.last_name,

d.department_i

d,

d.department_n

ame

FROM employees

SELECT

e.first_name,

e.last_name,

d.department_id,

d.department_name

FROM employees e

FULL OUTER JOIN departments d

ON

(e.department_id=d.department

_id);

SELECT

e.first_name,

e.last_name,

department_id,

d.department_n

ame

FROM employees

e

Não

aplicável

Page 17: Comandos sql e exercicios avançados

e, departments

d

WHERE

e.department_i

d

=d.department_

id (+)

UNION

SELECT

e.first_name,

e.last_name,

d.department_i

d,

d.department_n

ame

FROM employees

e, departments

d

WHERE

e.department_i

d (+)

=d.department_

id;

FULL OUTER

JOIN

departments d

USING

(department_id

);

A sintaxe proprietária não permite a junção externa em simultâneo à

esquerda e à direita, pelo que recorremos ao operador UNION que faz a

junção vertical dos dois conjuntos resultado;

O recurso ao operador UNION na sintaxe proprietária força a execução

de 2 queries para resolver o problema, ao contrário dos exemplos com

sintaxe SQL99 que resolvem com um único query;

Junção Vertical - Operadores sobre

conjuntos

O SQL disponibiliza três operadores que permitem a partir de dois conjuntos

produzir um terceiro. Estes operadores actuam sobre as colunas das tabelas de

origem, produzindo uma Junção Vertical:

UNIÃO

INTERSECÇÃO

DIFERENÇA

Page 18: Comandos sql e exercicios avançados

Estes operadores permitem obter um único conjunto resultado a partir de dois

conjuntos iniciais. São úteis na junção de diferentes consultas que se refiram a

tabelas diferentes mas que tenham colunas com o mesmo tipo de dados. A sua

utilizaçãoo segue as seguintes regras:

1. As duas instruções SELECT têm que seleccionar o mesmo número de

colunas;

2. As colunas correspondentes têm que ser do mesmo tipo de dados;

3. As linhas duplicadas são eliminadas pelo operador UNION. Se usarmos

UNION ALL os duplicados não são eliminados, o que poupa uma

operação de SORT;

4. Os nomes de coluna da primeira consulta aparecem no resultado;

5. A cláusula ORDER BY aparece no fim da instrução. Se as colunas

correspondentes dos diferentes SELECT's tiverem nomes diferentes, a

referêcia à(s) coluna(s) deverá ser feita por número de posição;

6. As instruções SELECT são executadas de cima para baixo;

7. Se forem utilizados vários operadores de conjunto é possivel

usar parentesis para definir prioridades de execução;

UNIÃO (UNION)

O operador UNION permite obter a reunião de dois conjuntos:

O comando abaixo apresenta os nomes e salários dos empregados das tabelas

EMP e EMPLOYEES. São duas consultas unidas numa única.

select ename, sal

from emp

UNION

select first_name, salary

from employees;

Page 19: Comandos sql e exercicios avançados

O comando UNION elimina automaticamente as linhas repetidas. Se

pretendermos que estas apareçam teremos que utilizar UNION ALL:

select job

from emp

UNION

select job_id

from employees;

JOB

----------

AC_ACCOUNT

AC_MGR

AD_ASST

AD_PRES

AD_VP

ANALYST

CLERK

FI_ACCOUNT

FI_MGR

HR_REP

IT_PROG

MANAGER

MK_MAN

MK_REP

PRESIDENT

PR_REP

PU_CLERK

PU_MAN

SALESMAN

SA_MAN

SA_REP

SH_CLERK

ST_CLERK

ST_MAN

24 rows selected

select job

from emp

UNION ALL

select job_id

from employees;

JOB

----------

CLERK

SALESMAN

SALESMAN

MANAGER

SALESMAN

MANAGER

MANAGER

ANALYST

PRESIDENT

SALESMAN

CLERK

CLERK

Page 20: Comandos sql e exercicios avançados

ANALYST

CLERK

AC_ACCOUNT

AC_MGR

AD_ASST

AD_PRES

AD_VP

AD_VP

FI_ACCOUNT

FI_ACCOUNT

FI_ACCOUNT

FI_ACCOUNT

FI_ACCOUNT

FI_MGR

HR_REP

IT_PROG

IT_PROG

IT_PROG

IT_PROG

IT_PROG

MK_MAN

MK_REP

PR_REP

PU_CLERK

PU_CLERK

PU_CLERK

PU_CLERK

PU_CLERK

PU_MAN

SA_MAN

SA_MAN

SA_MAN

SA_MAN

SA_MAN

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

SA_REP

Page 21: Comandos sql e exercicios avançados

SA_REP

SA_REP

SA_REP

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

SH_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_CLERK

ST_MAN

ST_MAN

ST_MAN

ST_MAN

ST_MAN

121 rows selected

O operador UNION ALL não respeita a teoria de conjuntos, pois

permite a repetição de elementos. Foi criado por uma questão de

Page 22: Comandos sql e exercicios avançados

desempenho, já que evita ao motor de base de daos as operações

ordenação e filtro. Deve ser usado quando não faça difrença trabalhar

com elementos repetidos;

Topo

INTERSECÇÃO (INTERSECT)

O operador INTERSECT permite obter a intersecção entre dois conjuntos:

O comando abaixo permite listar os salários que existem em EMP e em

EMPLOYEES:

select sal

from emp

INTERSECT

select salary

from employees;

SAL

----------------------

3000

1 rows selected

Topo

DIFERENÇA (MINUS)

O operador MINUS permite obter a diferença entre dois conjuntos:

Page 23: Comandos sql e exercicios avançados

O comando abaixo permite listar os salários que estão em EMP e não estão em

EMPLOYEES:

select sal

from emp

MINUS

select salary

from employees;

SAL

----------------------

800

950

1100

1250

1300

1500

1600

2450

2850

2975

5000

11 rows selected

Exercícios sobre junção de tabelas

1. Liste todos os empregados e respectivos nomes de departamento, por

ordem do nome de departamento;

2. Liste o nome, número e nome de departamento de todos os

empregados;

3. Liste o nome, departamento e localidade do departamento dos

empregados cujo salário mensal seja superior a 1500;

4. Produza uma lista que mostre o nível salarial (GRADE) de cada

empregado;

5. Produza uma lista dos empregados com nível salarial 3;

6. Mostre todos os empregados de Dallas (Dept Loc);

7. Liste nome, função, salário, nível (GRADE) e nome de departamento

para todos os empregados, excepto empregados de escritório (CLERK).

Ordene por salário apresentando primeiro o mais elevado;

Page 24: Comandos sql e exercicios avançados

8. Liste o nome, função, salário anual, número de departamento, nome de

departamento e nível salarial (grade) para os empregados que ganham

mais de 36000 por ano (14 vezes o salário mais a comissão) ou que

sejam empregados de escritório (CLERK);

9. Liste todos os departamentos que não têm empregados;

10. Liste o nome e número de empregado juntamente com o nome e

número do seu manager;

11. Modifique a solução da questão 10 de forma a visualizar KING que não

tem manager;

12. Encontre o departamento que contratou funcionários na primeira

metade de 1981 e na primeira metade de 1982;

13. Encontre todos os empregados que entraram para a empresa antes do

seu manager;

14. Encontre um outro método de consulta para a questãoa 9 utilizando o

operador MINUS.

Soluções dos exercícios sobre junção de

tabelas

15.

16. 1.

17. Solução usando a sintaxe tradicional: 18. select e.ename, e.deptno, d.dname

19. from emp e, dept d

20. where e.deptno=d.deptno

21. order by d.dname;

22. Solução usando a sintaxe SQL99: 23. select e.ename, e.deptno, d.dname

24. from emp e

25. inner join dept d on (e.deptno=d.deptno)

26. order by d.dname;

27.

28. 2.

29. Solução usando a sintaxe tradicional: 30. select e.ename, d.deptno, d.dname

31. from emp e, dept d

32. where e.deptno=d.deptno;

33. Solução usando a sintaxe SQL99: 34. select e.ename, d.deptno, d.dname

35. from emp e

36. inner join dept d on (e.deptno=d.deptno);

37.

38. 3.

39. Solução usando a sintaxe tradicional: 40. select e.ename,d.dname,d.loc

Page 25: Comandos sql e exercicios avançados

41. from emp e, dept d

42. where e.deptno = d.deptno

43. and sal > 1500;

44. Solução usando a sintaxe SQL99: 45. select e.ename,d.dname,d.loc

46. from emp e

47. inner join dept d on (e.deptno = d.deptno)

48. where sal > 1500;

49.

50. 4.

51. Solução usando a sintaxe tradicional: 52. select ename, job, sal, grade

53. from emp, salgrade

54. where sal between salgrade.losal and salgrade.hisal;

55. Solução usando a sintaxe SQL99: 56. select ename, job, sal, grade

57. from emp

58. inner join salgrade on (sal between salgrade.losal and

salgrade.hisal);

59.

60. 5.

61. Solução usando a sintaxe tradicional: 62. select ename, job, sal, grade

63. from emp, salgrade

64. where sal between salgrade.losal and salgrade.hisal

65. and grade = 3;

66. Solução usando a sintaxe SQL99: 67. select ename, job, sal, grade

68. from emp

69. inner join salgrade on (sal between salgrade.losal and

salgrade.hisal)

70. where grade = 3;

71.

72. 6.

73. Solução usando a sintaxe tradicional: 74. select emp.ename, emp.sal, dept.loc

75. from emp, dept

76. where emp.deptno=dept.deptno

77. and dept.loc = 'DALLAS';

78. Solução usando a sintaxe SQL99: 79. select emp.ename, emp.sal, dept.loc

80. from emp

81. inner join dept on (emp.deptno=dept.deptno)

82. where dept.loc = 'DALLAS';

83.

84. 7.

85. Solução usando a sintaxe tradicional: 86. select e.ename, e.job, e.sal, s.grade, d.dname

87. from emp e, salgrade s, dept d

88. where e.sal between s.losal and s.hisal

89. and e.deptno=d.deptno

90. and e.job !='CLERCK'

Page 26: Comandos sql e exercicios avançados

91. order by e.sal desc;

92. Solução usando a sintaxe SQL99: 93. select e.ename, e.job, e.sal, s.grade, d.dname

94. from emp e

95. inner join salgrade s on (e.sal between s.losal and s.hisal)

96. inner join dept d on (e.deptno=d.deptno)

97. where e.job != 'CLERCK'

98. order by e.sal desc;

99.

100. 8.

101. Solução usando a sintaxe tradicional: 102. select e.ename, e.job, e.sal*14+nvl(e.comm,0), d.deptno,

d.dname, s.grade

103. from emp e, salgrade s, dept d 104. where e.sal between s.losal 105. and s.hisal and e.deptno=d.deptno 106. and (e.sal*14+nvl(e.comm,0) > 36000 or e.job = 'CLERK');

107. Solução usando a sintaxe SQL99: 108. select e.ename, e.job, e.sal, s.grade, d.dname 109. from emp e 110. inner join salgrade s on (e.sal between s.losal and s.hisal) 111. inner join dept d on (e.deptno=d.deptno) 112. where e.job != 'CLERCK' 113. order by e.sal desc;

114.

115. 9.

116. Solução usando a sintaxe tradicional. O operador (+) coloca-se

sempre do lado fraco, isto é, do lado onde pode haver uma linha NULL: 117. select d.deptno, d.dname 118. from emp e, dept d 119. where e.deptno (+) = d.deptno 120. and e.ename is null;

121. Solução usando a sintaxe SQL99: 122. select d.deptno, d.dname 123. from emp e 124. right outer join dept d on (e.deptno = d.deptno) 125. where e.ename is null;

126.

127. 10.

128. Solução usando a sintaxe tradicional: 129. select e.empno as empregado_numero, e.ename as empregrado_nome, 130. m.empno as manager_numero, m.ename as manager_nome 131. from emp e, emp m 132. where e.mgr = m.empno;

133. Solução usando a sintaxe SQL99: 134. select e.empno as empregado_numero, e.ename as empregrado_nome, 135. m.empno as manager_numero, m.ename as manager_nome 136. from emp e 137. inner join emp m on (e.mgr = m.empno)

138.

Page 27: Comandos sql e exercicios avançados

139. 11.

140. Solução usando a sintaxe tradicional. O operador (+) coloca-se

sempre do lado fraco, isto é, do lado onde pode haver uma linha NULL. 141. select e.ename,e.empno,e.mgr,m.ename,m.empno 142. from emp e, emp m 143. where e.mgr=m.empno (+);

144. Solução usando a sintaxe SQL99: 145. select e.ename,e.empno,e.mgr,m.ename,m.empno 146. from emp e 147. left outer join emp m on (e.mgr=m.empno (+));

148.

149. 12.

150. Solução usando a sintaxe tradicional: 151. select e.deptno, d.dname 152. from emp e, dept d 153. where e.deptno=d.deptno 154. and e.hiredate between to_date('1981-01-01','YYYY-MM-DD') and

to_date('1981-06-30','YYYY-MM-DD')

155. intersect 156. select e.deptno, d.dname 157. from emp e, dept d 158. where e.deptno=d.deptno 159. and hiredate between to_date('1982-01-01','YYYY-MM-DD') and

to_date('1982-06-30','YYYY-MM-DD');

160. Usando sintaxe SQL99 161. select e.deptno, d.dname 162. from emp e 163. inner join dept d on (e.deptno=d.deptno) 164. where e.hiredate between to_date('1981-01-01','YYYY-MM-DD') and

to_date('1981-06-30','YYYY-MM-DD')

165. intersect 166. select e.deptno, d.dname 167. from emp e 168. inner join dept d on (e.deptno=d.deptno) 169. where hiredate between to_date('1982-01-01','YYYY-MM-DD') and

to_date('1982-06-30','YYYY-MM-DD');

170.

171. 13.

172. Solução usando a sintaxe tradicional: 173. select e.ename as empregado_nome, e.hiredate as empregado_data, 174. m.ename as manager_nome, m.hiredate as manager_data 175. where e.mgr=m.empno 176. and e.hiredate > m.hiredate;

177. Usando sintaxe SQL99 178. select e.ename as empregado_nome, e.hiredate as empregado_data, 179. m.ename as manager_nome, m.hiredate as manager_data 180. from emp e 181. inner join emp m on (e.mgr=m.empno) 182. and e.hiredate > m.hiredate;

183.

184. 14.

185. Retirar à lista dos departamentos aqueles que têm empregados.

186. Solução usando a sintaxe tradicional:

Page 28: Comandos sql e exercicios avançados

187. select deptno, dname 188. from dept 189. minus 190. select e.deptno, d.dname 191. from emp e, dept d 192. where e.deptno=d.deptno;

193. Usando sintaxe SQL99 194. select deptno, dname 195. from dept 196. minus 197. select e.deptno, d.dname 198. from emp e 199. inner join dept d on (e.deptno=d.deptno);

Subconsultas simples e correlacionadas

Uma subconsulta é uma instrução SELECT que está encadeada dentro de

outra instrução SELECT. A consulta interior é designada por selecção interna

e é executada em primeiro lugar, sendo o seu resultado utilizado para

completar a consulta principal ou externa. A utilização de subconsultas

permite construir comandos potentes a partir de comandos mais simples.

Na construção de subconsultas devem ser seguidas as seguintes linhas de

orientação:

A consulta interna deverá ser colocada entre parentisis e terá que estar

no lado direito da condição;

Desde a versão 8.1.7 do motor Oracle que as subconsultas podem ter

uma cláusula ORDER BY;

As subconsultas podem devolver uma ou mais linhas, uma ou mais

colunas. Se devolverem várias colunas então terão que estar pela

mesma ordem que as colunas que apareçam na cláusula de condição da

consulta externa (ou principal). O tipo de dados e o número de colunas

têm também que corresponder;

As subconsultas podem utilizar operadores lógicos, operadores de SQL,

operadores ANY e ALL e operadores de conjunto;

As subconsultas podem ser utilizadas em instruções de SELECT,

UPDATE, DELETE, INSERT e CREATE TABLE;

Existem dois tipos de subconsultas:

Simples

Correlacionadas

Subconsulta simples

Page 29: Comandos sql e exercicios avançados

Suponha que pretende encontrar o(s) empregado(s) que ganha(m) o salário

mais baixo na empresa, desconhecendo a quantia em causa. O problema pode

resolver-se em dois passos:

Encontrar o salário mais baixo;

Encontrar o(s) empregado(s) que ganha(m) o salário mais baixo;

Para o primeiro passo usamos a consulta abaixo:

select min(sal)

from emp;

MIN(SAL)

----------------------

800

1 rows selected

Para o segundo passo usamos a consulta abaixo:

select ename, job, sal

from emp

where sal = 800;

ENAME JOB SAL

---------- --------- ----------------------

SMITH CLERK 800

1 rows selected

Os dois queries podem ser juntos num só, usando uma subconsulta:

select ename, job, sal

from emp

where sal = (select min(sal)

from emp);

ENAME JOB SAL

---------- --------- ----------------------

SMITH CLERK 800

1 rows selected

A consulta interior é executada em primeiro lugar produzindo um resultado

(800). O bloco externo é depois executado, utilizando o valor devolvido pela

consulta interna para completar a sua condição de pesquisa.

O exemplo abaixo procura todos os empregados que executam a mesma

função que BLAKE:

select ename,job

from emp

Page 30: Comandos sql e exercicios avançados

where job = (select job

from emp

where ename = 'BLAKE');

EENAME JOB

---------- ---------

JONES MANAGER

BLAKE MANAGER

CLARK MANAGER

3 rows selected

Na consulta anterior o programador corre um risco: a consulta interna

pesquisa por nome, que não é uma coluna UNIQUE e portanto não há

garantias de que devolva apenas um valor. Se devolver mais que um

valor a consulta toda dá um erro, por causa da comparação feita com =;

Para resolver este problema podemos recorrer ao operador ANY, que

será visto mais à frente;

Topo

Subconsultas correlacionadas

Uma subconsulta correlacionada é executada de forma diferente da

subconsulta simples. Nestas consultas o subquery precisa de um dado que vem

do query principal, pelo que o SELECT interno é executado tantas vezes

quantas as linhas que são processadas no query principal.

A lista abaixo contem os passos necessários para executar uma consulta

correlacionada:

Obter uma linha candidata a partir da consulta externa;

Executar a consulta interna utilizando o valor da linha candidata;

Utilizar o valor ou valores resultantes da consulta interna para

qualificar ou desqualificar a linha candidata;

Repetir para a próxima linha até que não haja mais linhas candidatas;

A consulta abaixo pretende encontrar os empregados que ganham um salário

superior ao salário médio do respectivo departamento:

select e1.empno, e1.ename, e1.sal, e1.deptno

from emp e1

where e1.sal > (select avg(e2.sal)

from emp e2

where e2.deptno = e1.deptno);

Page 31: Comandos sql e exercicios avançados

EMPNO ENAME SAL DEPTNO

---------------------- ---------- ---------------------- -------------

---------

7499 ALLEN 1600 30

7566 JONES 2975 20

7698 BLAKE 2850 30

7788 SCOTT 3000 20

7839 KING 5000 10

7902 FORD 3000 20

6 rows selected

Para identificar que se trata de uma subconsulta correlacionada é

preciso reparar na utilização de uma coluna da tabela do SELECT

externo na cláusula WHERE do SELECT interno;

O pseudónimo de tabela é utilizado para evitar ambiguidade nos nomes

das colunas, pois tanto a consulta interna como a externa usam a

mesma tabela;

Subconsulta que devolve várias linhas -

operadores do SQL

Os exemplos de subconsulta que vimos até agora devolvem apenas uma linha.

Mas uma subconsulta pode devolver várias linhas, o que obriga a consulta

externa a ter cuidados especiais e a recorrer aos operadores do SQL:

IN

ANY (SOME)

ALL

EXISTS

IN

Quando a subconsulta devolve várias linhas o operador IN pode ser usado

para validar se uma linha da consulta externa está presente no conjunto criado

pela subconsulta. Devolve TRUE se o(s) valor(es) usado(s) na consulta

externa está(ão) incluído(s) no conjunto devolvido pela consulta interna. Este

operador pode ser negado com NOT.

Page 32: Comandos sql e exercicios avançados

Suponha que pretende determinar os empregados que ganham o salário mais

baixo em cada função, usando a tabela EMPLOYEES. A consulta abaixo tenta

responder a esta pergunta, mas sem sucesso.

select first_name,salary,job_id

from employees

where salary in (select min(salary)

from employees

group by job_id);

FIRST_NAME SALARY JOB_ID

-------------------- ---------------------- ----------

Nandita 4200 SH_CLERK

Diana 4200 IT_PROG

Shelley 12000 AC_MGR

Alberto 12000 SA_MAN

Nancy 12000 FI_MGR

William 8300 AC_ACCOUNT

Kevin 5800 ST_MAN

Ellen 11000 SA_REP

Gerald 11000 SA_MAN

Den 11000 PU_MAN

Jennifer 4400 AD_ASST

Lex 17000 AD_VP

Neena 17000 AD_VP

Randall 2500 SH_CLERK

Martha 2500 SH_CLERK

Peter 2500 ST_CLERK

Joshua 2500 ST_CLERK

James 2500 ST_CLERK

Karen 2500 PU_CLERK

Luis 6900 FI_ACCOUNT

Clara 10500 SA_REP

Eleni 10500 SA_MAN

Michael 13000 MK_MAN

Hermann 10000 PR_REP

Harrison 10000 SA_REP

Janette 10000 SA_REP

Peter 10000 SA_REP

Steven 24000 AD_PRES

Sundita 6100 SA_REP

Pat 6000 MK_REP

Bruce 6000 IT_PROG

TJ 2100 ST_CLERK

Susan 6500 HR_REP

Shanta 6500 ST_MAN

34 rows selected

O resultado obtido não está correcto pois a consulta nãogarante que o

salário mínimo encontrado na subconsulta se refere ao departamento

para o qual trabalha o empregado da consulta externa;

Page 33: Comandos sql e exercicios avançados

O salário mínimo da função SA_MAN é 10500. O empregado Alberto,

com a função SA_MAN ganha 12000, que é acima do mínimo, mas

aparece no resultado da consulta. Isto porque o seu salário é igual ao

mínimo da função FI_MGR;

A forma de associar a função ao salário mínimo é devolver e comparar o par

de valores, como no exemplo abaixo:

select first_name,salary,job_id

from employees

where (job_id,salary) in (select job_id,min(salary)

from employees

group by job_id);

FIRST_NAME SALARY JOB_ID

-------------------- ---------------------- ----------

Diana 4200 IT_PROG

Shelley 12000 AC_MGR

William 8300 AC_ACCOUNT

Kevin 5800 ST_MAN

Den 11000 PU_MAN

Jennifer 4400 AD_ASST

Neena 17000 AD_VP

Lex 17000 AD_VP

Martha 2500 SH_CLERK

Randall 2500 SH_CLERK

Luis 6900 FI_ACCOUNT

Nancy 12000 FI_MGR

Karen 2500 PU_CLERK

Eleni 10500 SA_MAN

Michael 13000 MK_MAN

Hermann 10000 PR_REP

Steven 24000 AD_PRES

Sundita 6100 SA_REP

Pat 6000 MK_REP

TJ 2100 ST_CLERK

Susan 6500 HR_REP

21 rows selected

Topo

ANY (SOME)

O operador ANY (e o seu sinónimo SOME) permite a uma consulta externa

fazer comparações usando < ou > com os elementos de um conjunto

devolvido pela subconsulta. Este operador devolve TRUE se uma das linhas

do conjunto satisfaz a condição, ou seja, devolve FALSE se nenhuma satisfaz

a condição. Este operador pode ser negado com NOT.

Page 34: Comandos sql e exercicios avançados

A consulta abaixo devolve os empregados que ganham mais que algum

empregado do departamento 30. Isto é o mesmo que afirmar que procuramos

os que ganham mais que o salário mínimo do departamento 30.

select ename, sal, job, deptno

from emp

where sal > ANY (select distinct sal

from emp

where deptno=30);

ENAME SAL JOB DEPTNO

---------- ---------------------- --------- ----------------------

KING 5000 PRESIDENT 10

FORD 3000 ANALYST 20

SCOTT 3000 ANALYST 20

JONES 2975 MANAGER 20

BLAKE 2850 MANAGER 30

CLARK 2450 MANAGER 10

ALLEN 1600 SALESMAN 30

TURNER 1500 SALESMAN 30

MILLER 1300 CLERK 10

WARD 1250 SALESMAN 30

MARTIN 1250 SALESMAN 30

ADAMS 1100 CLERK 20

12 rows selected

A comparação '= ANY' pode ser usada, mas é equivalente a IN;

Ao utilizar ANY é frequente recorrer à cláusula DISTINCT, para evitar

linhas repetidas e assim tornar a avaliação da expressão lógica mais

eficiente;

Topo

ALL

O operador ALL permite a uma consulta externa fazer comparações usando <

ou > com os elementos de um conjunto devolvido pela subconsulta. Este

operador devolve TRUE se todas as linhas do conjunto satisfazem a condição,

ou seja, devolve FALSE se alguma linha não a satisfaz. Este operador pode

ser negado com NOT.

A consulta abaixo devolve os empregados que ganham mais que todos os

empregados do departamento 30. Isto é o mesmo que afirmar que procuramos

os que ganham mais que o salário máximo do departamento 30.

Page 35: Comandos sql e exercicios avançados

select ename, sal, job, deptno

from emp

where sal > ALL (select distinct sal

from emp

where deptno=30);

ENAME SAL JOB DEPTNO

---------- ---------------------- --------- ----------------------

JONES 2975 MANAGER 20

SCOTT 3000 ANALYST 20

KING 5000 PRESIDENT 10

FORD 3000 ANALYST 20

4 rows selected

Topo

EXISTS

O operador EXISTS permite à consulta externa verificar se a consulta interna

devolveu alguma linha. Não se preocupa com o valor das linhas, mas sim com

a cardinalidade do conjunto. Devolve TRUE se a cardinalidade for superior a

0 (zero) e FALSE caso seja igual a 0 (zero). Este operador pode ser negado

com NOT.

O exemplo abaixo procura os empregados que tenham pelo menos um

subordinado:

select m.empno,m.ename,m.job,m.deptno

from emp m

where exists (select e.empno

from emp e

where e.mgr=m.empno)

EMPNO ENAME JOB DEPTNO

---------------------- ---------- --------- ----------------------

7902 FORD ANALYST 20

7698 BLAKE MANAGER 30

7839 KING PRESIDENT 10

7566 JONES MANAGER 20

7788 SCOTT ANALYST 20

7782 CLARK MANAGER 10

6 rows selected

Temos uma subconsulta correlacionada, onde o SELECT externo

selecciona os managers (m - tabela manager) enquanto o SELECT

interno selecciona os empregados desse manager (e - tabela

empregado);

Page 36: Comandos sql e exercicios avançados

O exemplo abaixo procura todos os departamentos que não possuem

empregados:

select d.deptno, d.dname

from dept d

where not exists (select deptno

from emp e

where e.deptno=d.deptno);

DEPTNO DNAME

---------------------- --------------

40 OPERATIONS

1 rows selected

Recorremos uma subconsulta correlacionada. A conculta externa

percorre todos os departamentos. A consulta interna valida se o

departamento tem empregados.

O query interno necessita devolver um valor que não tem que ser a coluna que

está em causa. O exemplo anterior pode ser substituído por este:

select d.deptno, d.dname

from dept d

where not exists (select 1

from emp e

where e.deptno=d.deptno);

DEPTNO DNAME

---------------------- --------------

40 OPERATIONS

1 rows selected

Uma consulta ao dicionário de dados mostra que o relacionamento entre as

tabelas EMP e DEPT não está assegurado por uma restrição definida no motor

da base de dados. Isto significa que é possível que exista um empregado que

trabalhe num departamento que não esteja definido na tabela DEPT. A

consulta abaixo pesquisa isso:

select e.empno, e.ename, e.deptno

from emp e

where not exists (select deptno

from dept

where dept.deptno=e.deptno);

EMPNO ENAME DEPTNO

---------------------- ---------- ----------------------

0 rows selected

Page 37: Comandos sql e exercicios avançados

Subconsulta e cláusulas da instrução

SELECT

Uma subconsulta pode ser colocada em vários pontos da instrução SELECT:

WHERE

HAVING

FROM

SELECT

Subconsulta na cláusula WHERE

Nos exemplos apresentados até aqui a subconsulta aparece na cláusula

WHERE.

O exemplo abaixo procura todos os empregados que executam a mesma

função que BLAKE (subconsulta simples):

select ename,job

from emp

where job = (select job

from emp

where ename = 'BLAKE');

EENAME JOB

---------- ---------

JONES MANAGER

BLAKE MANAGER

CLARK MANAGER

3 rows selected

A consulta abaixo procura os empregados que ganham um salário superior ao

salário médio do respectivo departamento (subconsulta correlacionada):

select e1.empno, e1.ename, e1.sal, e1.deptno

from emp e1

where e1.sal > (select avg(e2.sal)

from emp e2

where e2.deptno = e1.deptno);

EMPNO ENAME SAL DEPTNO

---------------------- ---------- ---------------------- -------------

---------

7499 ALLEN 1600 30

7566 JONES 2975 20

7698 BLAKE 2850 30

7788 SCOTT 3000 20

Page 38: Comandos sql e exercicios avançados

7839 KING 5000 10

7902 FORD 3000 20

6 rows selected

Topo

Subconsulta na cláusula HAVING

Uma subconsulta pode ser colocada na cláusula HAVING.

O exemplo abaixo determina os departamentos cujo salário médio é superior

ao do departamento 30:

select deptno, avg(sal)

from emp

group by deptno

having avg(sal) > (select avg(sal)

from emp

where deptno=30);

DEPTNO AVG(SAL)

---------------------- ----------------------

20 2175

10 2916,666666666666666666666666666666666667

2 rows selected

A consulta interna determina o salário médio do departamento 30;

A consulta externa determina os salários médios de todos os

departamentos mas só mostra os que têm valor superior ao devolvido

pela subconsulta;

O exemplo abaixo determina a função com o salário médio mais elevado:

select job, avg(sal)

from emp

group by job

having avg(sal) = (select max(avg(sal))

from emp

group by job);

JOB AVG(SAL)

--------- ----------------------

PRESIDENT 5000

1 rows selected

Page 39: Comandos sql e exercicios avançados

A consulta interna começa por determinar o salário médio de cada

função, indo depois determinar o maior salário de entre as médias;

A consulta externa determina o nome da função e respectivo salário

médio, só mostrando aqueles que igualam o valor devolvido pela

subconsulta;

Topo

Subconsulta na cláusula FROM

Uma subconsulta pode ser colocada na cláusula FROM, o que é o mesmo que

referir uma view (vista) ou uma tabela virtual.

O exemplo abaixo determina os 5 empregados que têm o salário mais elevado:

select *

from (select ename, sal

from emp

order by sal desc)

where rownum <=5;

ENAME SAL

---------- ----------------------

KING 5000

SCOTT 3000

FORD 3000

JONES 2975

BLAKE 2850

5 rows selected

A subconsulta lista os salários por ordem decrescente, criando uma

tabela virtual;

A consulta externa lista a tabela virtual gerada anteriormente

considerando apenas as 5 primeiras linhas;

A consulta abaixo lista os salários máximos da tabela EMP e EMPLOYEES

usando apenas um query e mostrando os resultados numa linha única:

select

e1.c1,

e2.c1

from

Page 40: Comandos sql e exercicios avançados

(select max(sal) c1 from emp) e1 ,

(select max(salary) c1 from employees) e2;

C1 C1

---------------------- ----------------------

5000 24000

1 rows selected

Cada consulta interna devolve um valor que é considerado uma tabela

virtual. Cada tabela virtual recebe um nome (e1 e e2);

O valor devolvido em cada consulta interna é colocado numa coluna

com o nome c1;

A consulta externa produz o produto cartesiano entre as tabelas virtuais.

Cada uma tem uma linha, pelo que o resultado tem cardinalidade 1;

Topo

Subconsulta na cláusula SELECT

As subconsultas podem ser colocadas na cláusula SELECT.

O exemplo abaixo pretende listar os salários máximos da tabela EMP e

EMPLOYEES usando apenas um query e mostrando os resultados numa linha

única:

select

(select max(sal) from emp),

(select max(salary) from employees)

from

dual;

(SELECTMAX(SAL)FROMEMP) (SELECTMAX(SALARY)FROMEMPLOYEES)

----------------------- --------------------------------

5000 24000

1 rows selected

O exemplo abaixo considera apenas os empregados do departamento 20 que

executam a função 'CLERK'. Pretende mostrar o salário de cada um e

compará-lo com a média da função e a média do departamento.

select

ename,

sal,

job,

(select round(avg(sal)) from emp where job='CLERK') "Avg_Sal_Job",

Page 41: Comandos sql e exercicios avançados

deptno,

(select round(avg(sal)) from emp where deptno=20) "Avg_Sal_Deptno"

from emp

where job='CLERK'

and deptno=20;

ENAME SAL JOB Avg_Sal_Job

DEPTNO Avg_Sal_Deptno

---------- ---------------------- --------- ---------------------- ---

------------------- ----------------------

SMITH 800 CLERK 1038 20

2175

ADAMS 1100 CLERK 1038 20

2175

2 rows selected

A primeira subconsulta determina a média de salários para a função

enquanto a segunda determina a média do departamento;

A consulta exterior usa os valores anteriores no meio dos valores de

cada linha da tabela que satisfaz a condição de filtro;

Estas subconsultas são simples (não correlacionadas);

Vamos generalizar o exemplo anterior para todos os empregados da empresa,

o que nos obriga a considerar todas as funções e todos os departamentos.

Vamos recorrer a subconsultas correlacionadas.

select

e1.ename,

e1.sal,

e1.job,

(select round(avg(e2.sal)) from emp e2 where e2.job=e1.job)

"Avg_Sal_Job",

e1.deptno,

(select round(avg(e3.sal)) from emp e3 where e3.deptno=e1.deptno)

"Avg_Sal_Deptno"

from emp e1;

ENAME SAL JOB Avg_Sal_Job

DEPTNO Avg_Sal_Deptno

---------- ---------------------- --------- ---------------------- ---

------------------- ----------------------

SMITH 800 CLERK 1038 20

2175

ALLEN 1600 SALESMAN 1400 30

1567

WARD 1250 SALESMAN 1400 30

1567

JONES 2975 MANAGER 2758 20

2175

MARTIN 1250 SALESMAN 1400 30

1567

BLAKE 2850 MANAGER 2758 30

1567

Page 42: Comandos sql e exercicios avançados

CLARK 2450 MANAGER 2758 10

2917

SCOTT 3000 ANALYST 3000 20

2175

KING 5000 PRESIDENT 5000 10

2917

TURNER 1500 SALESMAN 1400 30

1567

ADAMS 1100 CLERK 1038 20

2175

JAMES 950 CLERK 1038 30

1567

FORD 3000 ANALYST 3000 20

2175

MILLER 1300 CLERK 1038 10

2917

14 rows selected

Repare como os sinónimos do nome de tabela são usados para

correlacionar as consultas e eliminar a ambiguidade na referência ao

nome das colunas;

Subconsulta e encadeamento múltiplo

Uma subconsulta pode receber outra, pelo que as subconsultas podem ser

encadeadas umas dentro das outras até um limite de 255 níveis (Oracle

SGBDR versão 8.0).

O exemplo abaixo determina o nome, função e data de admissão dos

empregados cujo salário é superior ao salário mais elevado do departamento

'SALES':

select ename, job, hiredate, sal

from emp

where sal > (select max(sal)

from emp

where deptno = (select deptno

from dept

where dname = 'SALES'));

ENAME JOB HIREDATE SAL

---------- --------- ------------------------- ----------------------

JONES MANAGER 81.04.02 2975

SCOTT ANALYST 82.12.09 3000

KING PRESIDENT 81.11.17 5000

FORD ANALYST 81.12.03 3000

4 rows selected

Page 43: Comandos sql e exercicios avançados

A subconsulta que será feita em primeiro lugar é a que está mais abaixo

e à direita e vai determinar o número do departamento que tem o nome

'SALES';

Em seguida vamos determinar a média de salários desse departamento;

Por último vamos determinar quais os empregados que ganham mais

que essa média;

Exercícios sobre subconsultas

1. Encontre os empregados que ganham o salário mais alto em cada tipo

de função (JOB).

2. Encontre os empregados que ganham o salário mais baixo em cada

função. Visualize o resultado por ordem crescente de salário.

3. Encontre os empregados mais recentes em cada departamento. Ordene

por data de admissão.

4. Encontre o nome, salário e número de departamento dos empregados

que ganham um salário maior que a média do respectivo departamento.

Ordene por número de departamento.

5. Liste todos os departamentos que não possuem empregados. Utilize

uma subconsulta.

6. Determine qual o departamento com o MAIOR encargo anual de

remunerações (14 vezes o salário mais a comissão).

7. Em que ano entraram mais pessoas para a empresa? Visualize o ano e

número de empregados.

8. Modifique a questão 4 para apresentar também o valor do salário médio

para o departamento.

9. Escreva uma consulta para visualizar um '*' ao lado da linha do

empregado mais recente. Visualize ENAME, HIREDATE e a coluna

(maxdate) com o '*'.

10. Quem são os 3 empregados mais bem pagos da companhia ?

Soluções dos exercícios sobre

subconsultas

11.

12. 1. 13. select job,ename,sal

14. from emp

15. where (sal,job) in (select max(sal),job

16. from emp

Page 44: Comandos sql e exercicios avançados

17. group by job);

18. 2. 19. select job,ename,sal

20. from emp

21. where (sal,job) in (select min(sal),job

22. from emp

23. group by job)

24. order by sal;

25. 3. 26. select ename,hiredate,deptno

27. from emp

28. where (hiredate,deptno) in (select max(hiredate),deptno

29. from emp

30. group by deptno)

31. order by hiredate;

32. 4. 33. select e.ename, e.sal, e.deptno

34. from emp e

35. where e.sal > (select avg(e2.sal)

36. from emp e2

37. where e2.deptno= e.deptno)

38. order by e.deptno;

39. 5. 40. select d.deptno, d.dname

41. from dept d

42. where not exists (select e.empno

43. from emp e

44. where e.deptno = d.deptno);

45. 6. 46. define anual = sal*14+nvl(comm,0);

47.

48. select deptno, sum(&anual)

49. from emp

50. group by deptno

51. having sum(&anual)= (select max(sum(&anual))

52. from emp

53. group by deptno);

54. 7. 55. select to_char(hiredate,'YYYY'), count(empno)

56. from emp

57. group by to_char(hiredate,('YYYY'))

58. having count(empno) = (select max(count(empno))

59. from emp

60. group by to_char(hiredate,'YYYY'));

61. 8. 62. select e.ename, e.sal, e.deptno, avg(a.sal)

63. from emp e, emp a

64. where e.deptno=a.deptno

65. and e.sal > (select avg(sal)

66. from emp

67. where deptno=e.deptno)

68. group by e.ename, e.sal, e.deptno;

69. Outra solução poderá ser a apresentada a seguir, que usa um subquery

na clausula SELECT: 70. select e.ename, e.sal, e.deptno, (select avg(e2.sal)

71. from emp e2

72. where e2.deptno= e.deptno)

73. from emp e

Page 45: Comandos sql e exercicios avançados

74. where e.sal > (select avg(e2.sal)

75. from emp e2

76. where e2.deptno= e.deptno)

77. order by e.deptno;

78. 9. 79. select ename, hiredate, '*'

80. from emp

81. where hiredate = (select max(hiredate) from emp)

82. union

83. select ename, hiredate, ' '

84. from emp

85. where hiredate <> (select max(hiredate) from emp);

86. 10. 87. select *

88. from (select *

89. from emp

90. order by sal desc)

91. where rownum <= 3

UPDATE

O comando UPDATE permite alterar linhas numa tabela. Como todos os

outros comandos do conjunto DML faz sempre parte de uma transacção.

Neste módulo vamos ver:

UPDATE

SELECT FOR UPDATE

Pseudo coluna ORA_ROWSCN

UPDATE

Uma instrução UPDATE permite alterar:

Uma coluna de uma linha da tabela;

Várias colunas de uma linha;

Uma coluna em várias linhas;

Várias colunas em várias linhas;

Para obter o que foi descrito acima usamos:

A cláusula SET para escolher a(s) coluna(s);

A cláusula WHERE para escolher a(s) linha(s);

O exemplo abaixo será analisado no módulo de criação de tabelas. Ele permite

criar uma tabela a partir do resultado de um comando SELECT. As restrições

existentes nas tabelas originais não são recriadas na nova tabela:

drop table EMP_SALS;

Page 46: Comandos sql e exercicios avançados

create table EMP_SALS (emp_name, salary, grade) as (

select e.ename, e.sal, s.grade

from emp e, salgrade s

where e.sal between s.losal and s.hisal

);

Error starting at line 1 in command:

drop table EMP_SALS

Error report:

SQL Error: ORA-00942: table or view does not exist

create table succeeded.

Se a tabela EMP_SALS não existir o comando DROP TABLE dá o

erro listado acima;

O comando abaixo altera o salário do empregado cujo nome é 'MILLER'. Se

existirem vários empregados com esse nome, todos são alterados.

update EMP_SALS

set salary = 3000

where EMP_NAME = 'MILLER';

1 rows updated

Como se pode ver no comando que cria a tabela EMP_SALS, a coluna

GRADE vem da tabela SALGRADE e representa o "nível" do salário. O

salário tem nível 1 se estiver entre 700 e 1200, tem nível 2 se estiver entre

1201 e 1400 e assim sucessivamente. No comando executado no exemplo

anterior a coluna GRADE pode ficar inconsistente após a actualização do

valor de salário.

No comando abaixo todos os empregados cujo nome começa por M recebem

um incremento salarial de 1000 e recorremos a uma subconsulta para

actualizar a coluna GRADE:

update EMP_SALS e

set e.salary = e.salary + 1000,

e.grade = ( select s.grade

from salgrade s

where e.salary between s.losal and s.hisal

)

where e.emp_name like 'M%';

2 rows updated

Page 47: Comandos sql e exercicios avançados

Neste UPDATE são actualizadas duas colunas e duas linhas;

A actualização da coluna GRADE recorre a uma subconsulta

correlacionada: o valor de SALARY do comando externo é usado para

pesquisar SALGRADE e devolver o valor de GRADE adequado para a

actualização;

Se a cláusula WHERE for omitida todas as linhas da tabela serão

alteradas.

A base de dados coloca um LOCK (bloqueio) sobre todas as linhas alteradas

pelo comando UPDATE. Este LOCK impede que outras sessões da base de

dados alterem estas linhas enquanto a transacção actual não terminar.

A existência de LOCKS pode gerar tempos de espera na base de dados pelo

que deve ser usada com muita cautela. O tempo em que o LOCK está activo

deve ser minimizado.

Vistas

Uma vista é uma a instrução SELECT que recebe um nome e pode ser

consultada como se fosse uma tabela. Na realidade não contem dados, pois

usa os da tabela que lhe dá suporte. As vistas permitem definir um nível de

abstracção, um nível superior de segurança ou, de uma forma mais genérica,

um ponto de vista. Uma vista pode mostrar uma parte de uma tabela, a soma

de duas tabelas ou qualquer outro conjunto de dados que se consiga obter com

uma instrução SELECT.

Neste módulo vamos ver os seguintes exemplos de vistas:

Com parte de uma tabela

Com JOIN

Com UNION entre duas tabelas

Ver o código da VIEW

Com parte de uma tabela

Page 48: Comandos sql e exercicios avançados

Vamos criar uma vista (VIEW) que faz uma selecção vertical e horizontal

sobre uma tabela:

create or replace view employee_v1 as

select

first_name,

last_name,

job_id,

salary,

commission

from

employee

where

department_id in (23,30);

Esta VIEW pode ser consultada com o seguinte comando:

select * from employee_v1;

Se dermos GRANT SELECT desta VIEW a outro utilizador, este só verá as

linhas e colunas por ela filtradas, pelo que esta técnica é frequentemente usada

para mostrar um subconjunto da tabela original.

Topo

Com JOIN

Vamos criar uma VIEW que faz a contagem de pessoas em cada

departamento. Como queremos mostrar o nome do departamento temos que

fazer um JOIN com a tabela DEPARTMENT:

create or replace view DEPT_COUNT_V1 (dept, count) as

select d.name, count(*)

from employee e

inner join department d on (d.department_id=e.department_id)

group by d.name;

No primeiro exemplo as colunas da VIEW foram definidas com o mesmo

nome das colunas da tabela original. Pelo contrário, neste exemplo o comando

que cria a VIEW define o nome das colunas. Para visualizar os dados da

VIEW fazemos:

select * from DEPT_COUNT_V1;

Topo

Page 49: Comandos sql e exercicios avançados

Com UNION entre duas tabelas

Vamos construir uma VIEW que faz a união vertical de dois conjuntos:

create or replace view salary_month_v1 as

select empno,

ename,

sal

from emp

union

select employee_id,

first_name || ' ' || last_name,

salary

from employee;

Para visualizar os dados da VIEW:

select * from salary_month_v1 where sal >= 3000;

Neste exemplo o query que consulta a VIEW leva uma cláusula WHERE, que

funciona como um filtro adicional ao query que define a vista.

Topo

Ver o código da VIEW

O código de uma VIEW pode ser consultado com um query a USER_VIEWS,

ALL_VIEWS ou DBA_VIEW. A primeira mostra os objectos do tipo VIEW

que o próprio utilizador criou (o utilizador que tem a sessão aberta). A

segunda mostra todos os objectos do tipo VIEW aos quais o utilizador actual

tem acesso de leitura, onde se inclui as VIEWS que lhe pertencem e aquelas

que embora pertençam a outros utilizadores, estes deram privilégios de leitura

ao utilizador actual. A terceira requer um privilégio especial e mostra todas as

VIEWS definidas dentro da base de dados.

select * from user_views;

select * from all_views;

select * from dba_views; /*requer privilégios especiais*/

A cláusula GROUP BY

A cláusula GROUP BY é usada para agrupar (ou agregar) as linhas da tabela

segundo um critério escolhido pelo utilizador, podendo depois ser aplicada

Page 50: Comandos sql e exercicios avançados

uma função de grupo a cada um dos grupos. Usando GROUP BY não é

possível seleccionar linhas individuais.

O exemplo abaixo determina o salário médio de cada função (JOB):

select job, avg(sal)

from emp

group by job;

JOB AVG(SAL)

--------- ----------------------

CLERK 1037,5

SALESMAN 1400

PRESIDENT 5000

MANAGER 2758,333333333333333333333333333333333333

ANALYST 3000

5 rows selected

É possível contar o número de linhas que cada grupo contém. Por exemplo,

quantas pessoas há em cada função?

select job, count(*)

from emp

group by job;

JOB COUNT(*)

--------- ----------------------

CLERK 4

SALESMAN 4

PRESIDENT 1

MANAGER 3

ANALYST 2

5 rows selected

As duas consultas anteriores podem ser fundidas. Nesse caso pretendemos

saber qual o salário médio de cada função e quantas pessoas nela trabalham:

select job, avg(sal), count(*)

from emp

group by job;

JOB AVG(SAL) COUNT(*)

--------- ----------------------------------------- ------------------

----

CLERK 1037,5 4

SALESMAN 1400 4

PRESIDENT 5000 1

MANAGER 2758,333333333333333333333333333333333333 3

ANALYST 3000 2

Page 51: Comandos sql e exercicios avançados

5 rows selected

Utilizando a cláusula WHERE podem-se excluir linhas antes de as dividir em

grupos. Por exemplo apresentar o salário médio de cada função (job)

excluindo os MANAGER:

select job, avg(sal), count(*)

from emp

where job != 'MANAGER'

group by job;

JOB AVG(SAL) COUNT(*)

--------- ---------------------- ----------------------

CLERK 1037,5 4

SALESMAN 1400 4

PRESIDENT 5000 1

ANALYST 3000 2

4 rows selected

Topo

A cláusula HAVING

A cláusula WHERE é utilizada no "modo de linha" para filtrar linhas

(restrição horizontal). A cláusula HAVING tem funções semelhantes no

"modo de grupo": serve para filtrar grupos quando o query possui um GROUP

BY.

Neste ponto veremos:

Como utilizar a cláusula HAVING?

Considerações sobre desempenho entre WHERE e HAVING;

Como utilizar a cláusula HAVING?

O exemplo abaixo determina o salário médio de todos os departamentos que

possuem mais de três pessoas:

select deptno, avg(sal), count(*)

Page 52: Comandos sql e exercicios avançados

from emp

group by deptno

having count(*) > 3;

DEPTNO AVG(SAL)

COUNT(*)

---------------------- ----------------------------------------- -----

-----------------

30 1566,666666666666666666666666666666666667 6

20 2175 5

2 rows selected

O exemplo abaixo determina as funções cujo salário mais alto é igual ou

superior a 3000:

select job,max(sal)

from emp

group by job

having max(sal) >= 3000;

JOB MAX(SAL)

--------- ----------------------

PRESIDENT 5000

ANALYST 3000

2 rows selected

A execução de uma consulta com WHERE, GROUP BY e HAVING segue os

passos abaixo:

1. WHERE - para filtrar as linhas individuais (não pode conter funções de

grupo);

2. GROUP BY - para agrupar as linhas seleccionadas no passo anterior;

3. HAVING - para excluir os grupos não requeridos;

A cláusula WHERE não pode incluir funções de grupo, pois a sua missão é

filtrar linhas e não grupos. Durante a sua execução os grupos ainda não estão

formados, pelo que as funções de grupo não podem ser calculadas.

O exemplo abaixo pretende determinar os departamentos cujo salário médio é

superior a 2000:

select deptno,avg(sal)

from emp

where avg(sal) > 2000

group by deptno;

where avg(sal) > 2000

Page 53: Comandos sql e exercicios avançados

*

ERROR at line 3:

ORA-00934: group function is not allowed here

A mensagem de erro mostra o principio referido anteriormente: a cláusula

WHERE não pode receber funções de grupo. A forma de resolver este

problema é usando a cláusula HAVING:

select deptno,avg(sal)

from emp

group by deptno

having avg(sal)>2000;

DEPTNO AVG(SAL)

---------------------- -----------------------------------------

20 2175

10 2916,666666666666666666666666666666666667

2 rows selected

Topo

Considerações sobre desempenho entre WHERE e HAVING

Algumas consultas com grupos tanto podem ser resolvidas recorrendo a uma

cláusula WHERE como a uma cláusula HAVING. Considere que pretende

determinar o salário médio de cada função (JOB) excluíndo os 'MANAGER'.

A solução abaixo filtra as funções usando uma cláusula WHERE:

select job,avg(sal)

from emp

where job !='MANAGER'

group by job;

JOB AVG(SAL)

--------- ----------------------

CLERK 1037,5

SALESMAN 1400

PRESIDENT 5000

ANALYST 3000

4 rows selected

Page 54: Comandos sql e exercicios avançados

O mesmo resultado pode ser obtido recorrendo a um filtro de grupos, usando

HAVING:

select job,avg(sal)

from emp

group by job

having job != 'MANAGER';

JOB AVG(SAL)

--------- ----------------------

CLERK 1037,5

SALESMAN 1400

PRESIDENT 5000

ANALYST 3000

4 rows selected

A cláusula WHERE eliminou todos os elementos que iriam formar o conjunto

MANAGER, e por isso este não foi formado. Embora ambos queries tenham

resolvido o problema (foram eficazes), as duas consultas não são igualmente

eficientes.

A primeira consulta segue o seguinte plano de execução:

1. Identificar todas as linhas da tabela excluíndo as que têm função

MANAGER;

2. Ordenar as linhas por JOB;

3. Criar os conjuntos por JOB (agrupar);

4. Determinar a média de cada conjunto;

A segunda consulta segue este plano de execução:

1. Identificar todas as linhas da tabela;

2. Ordenar as linhas por JOB;

3. Criar os conjuntos por JOB (agrupar);

4. Determinar a média de cada conjunto;

5. Eliminar o conjunto 'MANAGER';

Comparando os dois planos de execução verificamos que no primeiro as

linhas são filtradas no inicio, o que significa que as operações de ordenação e

agrupamento são feitas sobre menos linhas. No segundo plano os grupos são

formados tendo em conta todas as linhas, sendo depois eliminado um dos

grupos. Quando as tabelas têem grande dimensão esta diferença é

significativa, tornando a primeira consulta mais eficiente pelos seguintes

motivos:

1. Tem menos linhas para ordenar;

Page 55: Comandos sql e exercicios avançados

2. Tem menos contas para fazer, já que há menos linhas e menos grupos;

3. Não deita fora trabalho já feito;

Exercícios sobre funções de grupo

1. Encontre o salário mais baixo de todos os empregados.

2. Encontre o salário mais baixo, mais alto e médio de todos os

empregados.

3. Determine o salário mais alto e mais baixo de cada tipo de função.

4. Determine quantos empregados tem a função MANAGER.

5. Determine o salário médio e a remuneração total média (14 vezes o

salário mais a comissão) para cada tipo de função. Não se esqueça que

os vendedores ganham comissão.

6. Determine a diferença entre o salário mais alto e mais baixo

7. Encontre todos os departamentos que têm mais de três empregados.

8. Verifique se todos os números de empregado são únicos.

9. Liste o salário mais baixo dentro dos empregados de cada manager

(MGR). Exclua os grupos em que o salário mais baixo é inferior a

1000. Ordene o resultado pelo salário.

Soluções dos exercícios sobre funções

de grupo

10.

11. 1. 12. select min(sal) from emp;

13.

14. 2. 15. select min(sal), max(sal), avg(sal) from emp;

16.

17. 3. 18. select job, max(sal), min(sal) from emp group by job;

19.

20. 4. 21. select count(*) from emp where job='MANAGER';

Page 56: Comandos sql e exercicios avançados

22.

23. 5. 24. select job, avg(sal), avg(sal*14+nvl(comm,0))

25. from emp

26. group by job;

27.

28. 6. 29. select max(sal)-min(sal) from emp;

30.

31. 7. 32. select deptno, count(*)

33. from emp

34. group by deptno

35. having count(*) > 3;

36.

37. 8. 38. select empno

39. from emp

40. group by empno

41. having count(*) > 1;

42.

43. 9. 44. select mgr, min(sal)

45. from emp

46. group by mgr

47. having min(sal)>=1000

48. order by min(sal);