Prévia do material em texto
Capítulo 6
Somando, contando e agrupando
Funções de agregação são extremamente úteis na confecção de consultas que efetuam cálculos
de consolidação da base de dados. Contar registros, obter o somatório de certos atributos,
calcular médias, máximos e mínimos são exemplos típicos das operações de agregação. Este
capítulo trata da construção de consultas SQL que empregam funções de agregação.
6.1 AGREGANDO VALORES
Como escrever uma consulta que obtenha a média das notas dos alunos? Com o que vimos até
aqui, é impraticável1 construir uma consulta dessas. É preciso somar os valores de todas as
notas e dividir esta soma pelo número de notas. Mesmo na álgebra relacional, vista no capítulo
3, não há operações com essas características.
Esta dificuldade levou os projetistas das linguagens de consulta a introduzir a idéia de funções
de agregação, que são funções aplicadas sobre colunas. A consulta SQL abaixo, que calcula a
média das notas dos alunos, é um exemplo
select avg (nota) as media_das_notas
from inscricao
que produz como resultado
media_das_notas
6,74285711560931
A função avg (da palavra average, que em inglês significa média) é um exemplo de função de
agregação. Uma função de agregação opera sempre sobre uma coluna ou uma expressão que
combine valores de uma ou mais colunas. No caso acima, a função avg operou sobre a coluna
nota, produzindo sua média.
Podemos conferir este resultado incluindo a soma e o número de notas existentes na base de
dados. A consulta
select sum (nota) as soma_das_notas,
count (nota) as numero_de_notas,
avg (nota) as media_das_notas
from inscricao
produz
soma_das_notas numero_de_notas media_das_notas
47,2 7 6,74285711560931
1 Impraticável, mas não impossível. Veja o exercício 6.10 no final deste capítulo.
2 Capítulo 6: Somando, contando e agrupando
Aqui usamos três funções de agregação na mesma consulta: avg, count e sum. A primeira
calcula a média, a segunda conta as notas e a terceira acumula a soma total. As três operam
sobre a coluna nota, independentemente.
Uma terceira versão da consulta, indicando também as notas mínima e máxima, com as funções
min e max, poderia ser escrita como
select sum(nota) as soma_das_notas,
count(nota) as numero_de_notas,
avg(nota) as media_das_notas,
min (nota) as nota_minima,
max (nota) as nota_maxima
from inscricao
produzindo
soma_das_notas numero_de_notas media_das_notas nota_minima nota_maxima
47,2 7 6,74285711560931 3 10
Note que somente sete notas, das nove existentes na base exemplo, foram computadas. Isso
ocorre porque valores nulos não são considerados.
6.2 A CLÁUSULA GROUP BY
Vamos supor agora que fosse necessário produzir estatísticas equivalentes, porém mostrando-as
por disciplina. Ou seja, é preciso produzir a média das notas por disciplina, a nota mínima por
disciplina, e assim por diante. Para simplificar, vamos calcular inicialmente somente a média
das notas. A consulta
select coddisciplina,
avg (nota) as nota_media_disciplina
from inscricao
group by coddisciplina
produz
coddisciplina nota_media_disciplina
112 NULL
114 7,00
117 4,20
210 9,50
316 6,50
317 6,75
A novidade nesta consulta é a cláusula group by, que estabelece que o resultado deve ser
agrupado pelos diferentes valores do atributo coddisciplina e que as funções de agregação sejam
aplicadas a cada um desses grupos separadamente.
A figura a seguir ilustra o agrupamento de registros que originou o resultado da consulta
anterior. O conteúdo da tabela INSCRIÇÃO, cujas colunas foram utilizadas no cômputo das
funções de agregação é mostrado por ordem do atributo coddisciplina. Para cada valor deste
atributo, que é pivô na cláusula group by, a função avg calcula a média das notas
correspondentes. O primeiro grupo inclui duas notas com valores nulos para a disciplina de
Error! Style not defined. 3
código 112, o que origina a primeira linha do resultado. A média de valores nulos é sempre
nula. O último grupo, para a disciplina 317, inclui duas notas que têm por média 6,75.
A mesma idéia, agora com as demais funções de agregação.
select coddisciplina,
sum (nota) as soma_das_notas,
count (nota) as numero_de_notas,
avg (nota) as media_das_notas,
min (nota) as nota_minima,
max (nota) as nota_maxima
from inscricao
group by coddisciplina
O resultado apresenta cada uma das estatísticas anteriores computadas separadamente para cada
diferente código de disciplina. Note que no caso de notas com valores nulos todos os resultados
calculados são nulos, exceto o de contagem (função count) que é zero.
coddisciplina soma_das_notas numero_de_notas media_das_notas nota_minima nota_maxima
112 NULL 0 NULL NULL NULL
114 7 1 7 7 7
117 4,2 1 4,2 4,2 4,2
210 9,5 1 9,5 9,5 9,5
316 13 2 6,5 3 10
317 13,5 2 6,75 5,5 8
De uma forma geral, valores nulos são ignorados pelas funções de agregação. Assim,
numa coluna onde haja valores nulos e não nulos somente os não nulos são considerados.
A CLÁUSULA GROUP BY NO MYSQL
O MySQL admite que uma coluna referenciada na cláusula group by seja identificada pela sua
posiçao relativa na lista-alvo ou seu alias. Assim, seria possível uma consulta que normalmente
seria escrita como
select coddisciplina,
avg (nota) as nota_media_disciplina
from inscricao
group by coddisciplina
poderia ser escrita como
select coddisciplina as dis,
avg (nota) as nota_media_disciplina
from inscricao
group by dis
Matricula CodDisciplina Nota
1001 112 NULL
1007 112 NULL
1007 114 7,00
1005 117 4,2
1002 210 9,5
1005 316 3,0
1010 316 10,00
1001 317 8,0
1010 317 5,5
coddisciplina nota_media_disciplina
112 NULL
114 7
117 4,2
210 9,5
316 6,5
317 6,75
4 Capítulo 6: Somando, contando e agrupando
ou como
select coddisciplina,
avg (nota) as nota_media_disciplina
from inscricao
group by 1
Nos três casos, os resultados são idênticos.
AGRUPANDO VALORES NULOS
Valores nulos que eventualmente ocorram nas expressões que determinam os grupamentos são
mantidos em grupos separados. Suponha a consulta que produz o número de alunos para os
quais cada diferente nota foi atribuída.
select nota, count(*) as alunos
from inscricao
group by nota
order by 1
No resultado
nota alunos
NULL 2
3 1
4,2 1
5,5 1
7 1
8 1
9,5 1
10 1
pode-se notar que as notas nulas foram grupadas num único grupo. Ou seja, para efeito da
cláusula group by, valores nulos são considerados iguais entre si. Um fato curioso neste
exemplo é que, no Oracle, o registro que contém o valor nulo é o último do resultado, enquanto
que para o Access, MySQL e SQL Server é o primeiro, como mostrado acima.
6.3 A CLÁUSULA HAVING
A cláusula having possibilita a filtragem de linhas do resultado após a aplicação das funções de
agregação. Vamos supor que, no exemplo acima, seja necessário mostrar apenas as disciplinas
que têm mais de uma nota. A consulta seria reescrita como
select coddisciplina,
sum (nota) as soma_das_notas,
count (nota) as numero_de_notas,
avg (nota) as media_das_notas,
min (nota) as nota_minima,
max (nota) as nota_maxima
from inscricao
group by coddisciplina
having count (nota) > 1
para produzir como resultado
coddisciplina soma_das_notas numero_de_notas media_das_notas nota_minima nota_maxima
Error! Style not defined. 5
316 13 2 6,5 3 10
317 13,5 2 6,75 5,5 8
Tanto a cláusula where como a cláusula having filtram linhas do resultado. A diferença é
que, na cláusula where, a expressão lógica deve ser verdadeira antes que uma
determinada combinação de registros possa formar uma linha do resultado; na cláusulahaving,
as linhas são filtradas depois das funções de agregação terem sido aplicadas sobre as colunas.
Este conceito ficará mais claro mais adiante neste capítulo, na seção que aborda a construção do
resultado de consultas com funções de agregação.
O critério de filtragem de uma cláusula having é uma expressão lógica genérica, porém sujeita à
seguinte restrição: somente um atributo ou expressão referenciado na cláusula group by pode
aparecer fora da aplicação de uma função agregada. Possibilidades adicionais são apresentadas
no capítulo 9.
Tomando-se como exemplo a consulta
select coddisciplina,
sum (nota) as soma_das_notas,
count (nota) as numero_de_notas,
avg (nota) as media_das_notas,
min (nota) as nota_minima,
max (nota) as nota_maxima
from inscricao
group by coddisciplina
nota-se que os termos lógicos aplicáveis a uma possível cláusula having somente poderiam fazer
referência ao atributo coddisciplina (porque aparece na cláusula group by). Os demais atributos
envolvidos presentes na fonte de registros (INSCRIÇÃO) poderiam ser referenciados apenas como
parte de uma função de agregação. Assim, a cláusula
having coddisciplina > 200 and max (nota) < 9
seria uma cláusula válida, porque nota aparece numa função de agregação, mas
having coddisciplina > 200 and nota < 9
não seria aceita, porque o atributo nota não é referenciado na cláusula group by.
6.4 COMBINANDO TABELAS E APLICANDO FUNÇÕES DE AGREGAÇÃO
O resultado da consulta anterior ficaria melhor se as disciplinas fossem identificadas pelos seus
nomes ao invés dos códigos. Para fazer isso, entretanto, é preciso combinar as tabelas INSCRIÇÃO
e DISCIPLINA. A consulta a seguir ilustra o uso de consultas com junções e funções de agregação.
select i.coddisciplina,
d.disciplina,
sum (i.nota) as soma_das_notas,
count (i.nota) as numero_de_notas,
avg (i.nota) as media_das_notas,
min (i.nota) as nota_minima,
max (i.nota) as nota_maxima
from inscricao i, disciplina d
where i.coddisciplina = d.coddisciplina
group by i.coddisciplina, d.disciplina
order by d.disciplina
O resultado produzido é mostrado abaixo.
coddisciplina disciplina soma_das_notas numero_de_notas media_das_notas nota_minima nota_maxima
210 Compiladores 9,5 1 9,5 9,5 9,5
112 Dir. Constitucional NULL 0 NULL NULL NULL
114 Direito Civil 7 1 7 7 7
6 Capítulo 6: Somando, contando e agrupando
117 Estatística 4,2 1 4,2 4,2 4,2
317 Português 13,5 2 6,75 5,5 8
316 Sociologia 13 2 6,5 3 10
Note que há dois atributos, coddisciplina e disciplina, referenciados na cláusula group by. Isto
porque os cálculos das funções de agregação devem ser efetuados para cada grupo definido por
esses dois atributos, em conjunto.
A consulta a seguir inclui no resultado as disciplinas que não têm inscrições e mostra como
qualquer das formas de junções apresentadas no capítulo 5 pode ser empregada em combinação
com as funções de agregação.
select d.coddisciplina,
d.disciplina,
sum (i.nota) as soma_das_notas,
count (i.nota) as numero_de_notas,
avg (i.nota) as media_das_notas,
min (i.nota) as nota_minima,
max (i.nota) as nota_maxima
from disciplina d left join inscricao i on i.coddisciplina = d.coddisciplina
group by d.coddisciplina, d.disciplina
order by d.disciplina
select d.coddisciplina,
d.disciplina,
sum (i.nota) as soma_das_notas,
count (i.nota) as numero_de_notas,
avg (i.nota) as media_das_notas,
min (i.nota) as nota_minima,
max (i.nota) as nota_maxima
from disciplina d, inscricao i
where d.coddisciplina =(+) i.coddisciplina
group by d.coddisciplina, d.disciplina
order by d.disciplina
Em função da operação de left outer join, o resultado agora inclui a disciplina Banco de Dados,
que havia ficado de fora antes porque não possui inscrições.
coddisciplina disciplina soma_das_notas numero_de_notas media_das_notas nota_minima nota_maxima
211 Bancos de Dados NULL 0 NULL NULL NULL
210 Compiladores 9,5 1 9,5 9,5 9,5
112 Dir. Constitucional NULL 0 NULL NULL NULL
114 Direito Civil 7 1 7 7 7
117 Estatística 4,24 1 4,24 4,2 4,2
317 Português 13,5 2 6,75 5,5 8
316 Sociologia 13 2 6,5 3 10
Note que as disciplinas Dir. Constitucional e Bancos de Dados têm, ambas, zero notas, mas por
motivos diferentes. A primeira, porque todos seus alunos têm notas com valores nulos; a
segunda, porque não há inscrição alguma para ela. O problema agora é escrever uma consulta
que faça distinção desses casos, e retorne as disciplinas que ainda não têm notas registradas,
porém possuem inscrições.
A consulta a seguir é uma das alternativas.
select d.disciplina,
count (i.matricula) as alunos_inscritos
from disciplina d, inscricao i
where i.coddisciplina = d.coddisciplina
group by d.disciplina
having count (i.nota) =0
Error! Style not defined. 7
e produz o resultado já esperado
disciplina alunos_inscritos
Dir. Constitucional 2
Note que neste exemplo a função de agregação count(i.nota) é utilizada exclusivamente para
filtrar as linhas do resultado final, pós-agregação, uma vez que o cálculo desta função não
aparece no resultado.
6.5 ENTENDENDO A CONSTRUÇÃO DO RESULTADO COM FUNÇÕES AGREGADAS
O primeiro passo para entender como o SQL constrói o resultado de uma consulta que contém
funções de agregação é tentar visualizar o resultado pré-agregação. Para isso, vamos construir
uma consulta que produza um resultado intermediário, sobre o qual as funções de agregação vão
atuar e computar seus resultados. A consulta que produz o resultado pré-agregação pode ser
derivada da consulta originalmente sendo analisada, através de transformações sucessivas, tal
que:
1. sejam removidas as funções de agregação, permanecendo na lista-alvo as expressões sobre
as quais essas funções são aplicadas;
2. sejam incluídas na lista-alvo as expressões sobre as quais aplicam-se as funções de
agregação mencionadas na cláusula order by;
3. seja removida a cláusula order by, se existir;
4. sejam incluídas na lista-alvo as expressões sobre as quais aplicam-se as funções de
agregação mencionadas na cláusula having;
5. seja removida a cláusula having, se existir; a cláusula group by deve ser substituída por uma
cláusula order by com os mesmos atributos;
6. a cláusula group by transforme-se numa cláusula order by.
Vamos tomar como exemplo a consulta vista no início da seção 6.4.
select i.coddisciplina,
d.disciplina,
sum (i.nota) as soma_das_notas,
count (i.nota) as numero_de_notas,
avg (i.nota) as media_das_notas,
min (i.nota) as nota_minima,
max (i.nota) as nota_maxima
from inscricao i, disciplina d
where i.coddisciplina = d.coddisciplina
group by i.coddisciplina, d.disciplina
order by d.disciplina
No primeiro passo, removemos as funções de agregação, mantendo na lista alvo as expressões
sobre as quais as funções são aplicadas. Para o passo 2 não há funções de agregação na cláusula
order by, que é removida no passo 3. Os passos 4 e 5 não alteram a consulta porque não há
cláusula having. O passo 6 transforma a cláusula group by numa cláusula order by. A consulta
agora é escrita como
select i.coddisciplina,
d.disciplina,
(i.nota) as soma_das_notas,
(i.nota) as numero_de_notas,
(i.nota) as media_das_notas,
(i.nota) as nota_minima,
(i.nota) as nota_maxima
from inscricao i, disciplina d
where i.coddisciplina = d.coddisciplina
order by i.coddisciplina, d.disciplina
8 Capítulo 6: Somando, contando e agrupando
e produz
coddisciplina disciplina soma_das_notas numero_de_notas media_das_notas nota_minima nota_maxima
112 Dir. ConstitucionalNULL NULL NULL NULL NULL
112 Dir. Constitucional NULL NULL NULL NULL NULL
114 Direito Civil 7,00 7,00 7,00 7,00 7,00
117 Estatística 4,20 4,20 4,20 4,20 4,20
210 Compiladores 9,50 9,50 9,50 9,50 9,50
316 Sociologia 3,00 3,00 3,00 3,00 3,00
316 Sociologia 10,00 10,00 10,00 10,00 10,00
317 Português 8,00 8,00 8,00 8,00 8,00
317 Português 5,50 5,50 5,50 5,50 5,50
Este é o resultado pré-agregação. O próximo passo da construção do resultado é identificar cada
um dos grupos de registros, separando-os a partir dos atributos coddisciplina e disciplina, e
aplicar as funções de agregação nas colunas correspondentes.
coddisciplina disciplina soma_das_notas numero_de_notas media_das_notas nota_minima nota_maxima
112 Dir. Constitucional NULL NULL NULL NULL NULL
NULL NULL NULL NULL NULL
114 Direito Civil 7,00 7,00 7,00 7,00 7,00
117 Estatística 4,20 4,20 4,20 4,20 4,20
210 Compiladores 9,50 9,50 9,50 9,50 9,50
316 Sociologia 3,00 3,00 3,00 3,00 3,00
10,00 10,00 10,00 10,00 10,00
317 Português 8,00 8,00 8,00 8,00 8,00
5,50 5,50 5,50 5,50 5,50
coddisciplina disciplina sum count avg min max
Após a aplicação das funções de agregação sobre cada coluna, de acordo com os grupos
identificados, temos
coddisciplina disciplina soma_das_notas numero_de_notas media_das_notas nota_minima nota_maxima
112 Dir. Constitucional NULL 0 NULL NULL NULL
114 Direito Civil 7,00 1 7,00 7,00 7,00
117 Estatística 4,2 1 4,20 4,20 4,20
210 Compiladores 9,5 1 9,50 9,50 9,50
316 Sociologia 13 2 6,50 3,00 10,00
317 Português 13,5 2 6,75 5,50 8,00
Para a obtenção do resultado final, basta filtrar as linhas do resultado com o critério da cláusula
having e ordená-lo pelos critérios da cláusula order by.
Há casos em que as cláusulas order by e having utilizam critérios que referenciam resultados de
funções de agregação diferentes dos que foram incluídos na lista-alvo da consulta. A título de
exemplo, vamos supor que seja necessário descobrir quais as disciplinas que ainda têm notas
incompletas e relacioná-las em ordem decrescente da média de suas notas. Podemos fazer isso
listando as disciplinas onde o número de inscrições é maior que o número de notas. Note que
nenhuma dessas contagens aparecem no resultado, sendo utilizadas exclusivamente na cláusula
having, o mesmo ocorrendo para a média das notas, utilizada como critério de ordenação.
select d.disciplina
from disciplina d, inscricao i
Error! Style not defined. 9
where i.coddisciplina = d.coddisciplina
group by d.disciplina
having count (i.matricula) > count (i.nota)
order by avg (i.nota) desc
Nesta consulta, o resultado pré-agregação deveria ser gerado por uma consulta como
select d.disciplina,
(i.matricula),
(i.nota),
(i.nota)
from disciplina d, inscricao i
where i.coddisciplina = d.coddisciplina
order by d.disciplina
Note que este resultado pré-agregação permite que tanto as notas como as matrículas das
inscrições sejam contadas. Isso é necessário porque as duas colunas fazem parte do critério de
filtragem da cláusula having.
disciplina matricula nota nota
Compiladores 1002 9,50 9,50
Dir. Constitucional 1001 NULL NULL
1007 NULL NULL
Direito Civil 1007 7,00 7,00
Estatística 1005 4,20 4,20
Português 1001 8,00 8,00
1010 5,50 5,50
Sociologia 1005 3,00 3,00
1010 10,00 10,00
disciplina count count avg
A aplicação das agregações indicadas acima resulta em
disciplina Count (matricula) Count (nota) Avg (nota)
Compiladores 1 1 9,50
Dir. Constitucional 2 0 NULL
Direito Civil 1 1 7,00
Estatística 1 1 4,20
Português 2 2 6,75
Sociologia 2 2 6,50
A filtragem final permite que apenas as linhas onde
count (i.matricula) > count (i.nota)
permaneçam no resultado. Finalmente, a lista de disciplinas que ainda têm notas não
preenchidas.
disciplina
Dir. Constitucional
10 Capítulo 6: Somando, contando e agrupando
AGREGANDO VALORES DISTINTOS
Em algumas implementações SQL, as funções de agregação sum, avg e count podem ser
aplicadas seletivamente, apenas sobre os diferentes valores que aparecem numa coluna. Para
ilustrar essa característica, vamos considerar a consulta abaixo que produz, para cada curso, a
nota média, o número de alunos inscritos, o número de inscrições e o número de notas.
select c.curso,
avg (i.nota) as nota_media,
count (distinct i.matricula) as numero_alunos,
count (i.matricula) as numero_inscricoes,
count (i.nota) as numero_notas
from aluno a, inscricao i, curso c
where a.matricula = i.matricula
and a.codcurso = c.codcurso
group by c.curso
order by c.curso
O resultado é mostrado a seguir.
curso nota_media numero_alunos numero_inscricoes numero_notas
Direito 7,5 2 4 2
Informática 9,5 1 1 1
Jornalismo 5,67499995231628 2 4 4
O número de alunos e o número de inscrições são diferentes em dois cursos, embora a mesma
função de agregação tenha sido utilizada sobre a mesma coluna. A diferença é que, no caso do
número de alunos, a contagem foi efetuada sobre valores distintos. No caso do curso de Direito,
há duas matrículas diferentes, embora cada uma delas apareça duas vezes. O resultado pré-
agregação desta consulta é
curso nota_media numero_alunos numero_inscricoes numero_notas
Direito NULL 1001 1001 NULL
8,00 1001 1001 8,00
NULL 1007 1007 NULL
7,00 1007 1007 7,00
Informática 9,50 1002 1002 9,50
Jornalismo 3,00 1005 1005 3,00
4,20 1005 1005 4,20
5,50 1010 1010 5,50
10,00 1010 1010 10,00
curso avg count distinct count count
AGREGANDO COM COUNT(*)
A função agregada count, como mostrada até aqui, fica suscetível à ocorrência de valores nulos,
pois esses não são computados. Quando é necessário fazer uma consulta que conte todos os
valores separadamente, mesmo que sejam nulos, é possível usar a construção count(*), que tem
o significado de “contar linhas”. Uma consulta que apenas contasse as inscrições, por
disciplina, poderia ser escrita como
select d.disciplina,
count (*) as numero_de_inscricoes
from disciplina d, inscricao i
Error! Style not defined. 11
where d.coddisciplina = i.coddisciplina
group by d.disciplina
order by 1
Pode-se imaginar o resultado pré-agregação como se a coluna onde a função count (*) é aplicada
fosse formada por todos os atributos que não são pivôs da cláusula group by. Ou seja, a função
contaria efetivamente o número de combinações de registros que correspondem a cada diferente
valor do atributo disciplina.
disciplina d.coddisciplina, d.codprofessor, d.chst, d.chsp, i.matricula, i.coddsciplina,
Compiladores ...
Dir. Constitucional ...
...
Direito Civil ...
Estatística ...
Português ...
...
Sociologia ...
...
disciplina count (*)
O resultado produzido é
disciplina numero_de_inscricoes
Compiladores 1
Dir. Constitucional 2
Direito Civil 1
Estatística 1
Português 2
Sociologia 2
Note que a consulta acima produz o mesmo resultado que
select d.disciplina,
sum (1) as numero_de_inscricoes
from disciplina d, inscricao i
where d.coddisciplina = i.coddisciplina
group by d.disciplina
order by 1
porque o resultado pré-agregação, obtido a partir da consulta
select d.disciplina,
(1) as numero_de_inscricoes
from disciplina d, inscricao i
where d.coddisciplina = i.coddisciplina
é
disciplina numero_de_inscricoes
Dir. Constitucional 1
1
Direito Civil 1
Estatística 1
Compiladores 1
Sociologia 1
12 Capítulo 6: Somando, contando e agrupando
1
Português 1
1
disciplina sum
CALCULANDO COM FUNÇÕES DE AGREGAÇÃO
Funções de agregação podem ser utilizadas em expressões aritméticas na lista-alvo. Vamos
considerar um exemplo onde é necessário produzir, para cada aluno, a média de suas notas
ponderada pelo número de créditos de cada disciplina. Ou seja, as disciplinas com mais créditos
pesam mais no cômputo da média.Os créditos são calculados a partir da carga horária de cada
disciplina. Em SQL não existe a noção de média ponderada e é preciso calculá-la
expressamente. A consulta
select a.nome,
sum (i.nota * (d.chst + d.chsp * 0.5)) / sum (d.chst + d.chsp * 0.5) as media_ponderada
from aluno a, inscricao i, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
group by a.nome
order by a.nome
faz isso e produz como resultado
nome media_ponderada
Barbara Carlito 3,554
Carlos Maradona 3,9
Maria Lucia Silva 7,6
Maria Rita Colatti 9,5
Ricardo Biondi 4
É interessante analisar o resultado pré-agregação desta consulta e verificar o que acontece no
seu processamento.
select a.nome,
(i.nota * (d.chst + d.chsp * 0.5)) as notas_creditos,
(d.chst + d.chsp * 0.5) as media_ponderada
from aluno a, inscricao i, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
order by a.nome
Na base de exemplos, a aluna Barbara Carlito está inscrita em duas disciplinas, Sociologia e
Estatística, de 3,5 (3�1+1�0,5) e 3 (2�1+2�0,5) créditos, respectivamente. Barbara tem notas
3 e 4,2 nessas disciplinas. Assim, na coluna referente à aplicação da função sum (i.nota * (d.chst +
d.chsp * 0.5)), aparecem os valores 10,5 e 12,6, que correspondem a 3 � 3,5 e 4,2 � 3. O valor de
sum (i.nota * (d.chst + d.chsp * 0.5)) para essa aluna é 23,1 e o valor de sum (d.chst + d.chsp * 0.5) é 6,5.
Logo, a média é 3,554, correspondente ao quociente 23,1 / 6,5.
nome notas_creditos media_ponderada
Barbara Carlito 10,5 3,5
Barbara Carlito 12,6 3
Carlos Maradona 35 5
Carlos Maradona 4
Maria Lucia Silva 22 4
Maria Lucia Silva 35 3,5
Maria Rita Colatti 38 4
Error! Style not defined. 13
Ricardo Biondi 32 4
Ricardo Biondi 4
O aluno Carlos Maradona tem uma inscrição em Dir. Constitucional, de 4 créditos, sem nota
(valor nulo) e outra inscrição em Direito Civil, de 5 créditos, com nota 7. A média é a divisão
de 35 por 9, porque a nota nula não contribui para o numerador da média. Contudo, o número
de créditos da disciplina sem nota foi computado no denominador. No capítulo 7, este exemplo
será revisitado, pois com a ajuda de uma função será possível eliminar do denominador os
créditos dessa disciplina sem nota.
FUNÇÕES DE AGREGAÇÃO E A CLÁUSULA WHERE
Um engano bastante freqüente de programadores SQL iniciantes é o de tentar utilizar funções de
agregação em termos lógicos da cláusula where. Vamos ilustrar esse caso com uma consulta
que requer a lista de alunos cujas notas sejam maiores ou iguais à média geral das notas.
Intuitivamente, muitos programadores tentariam resolver esta consulta como
select a.nome,
d.disciplina,
i.nota
from aluno a, inscricao i, disciplina d
where a.matricula=i.matricula
and i.coddisciplina = d.coddisciplina
and i.nota >= avg (d.nota)
order by 1
Esta construção não é aceita, porque fica impossível obter a consulta pré-agregação. Os termos
lógicos da cláusula where são avaliados para cada combinação das instâncias das variáveis de
registro SQL. Neste momento, não há colunas formadas ainda e a função de agregação não
pode ser aplicada.
No capítulo 9 esta consulta é formulada através da utilização de subconsultas SQL. Por ora, fica
estabelecida uma regra básica: funções de agregação não podem aparecer em termos lógicos na
cláusula where, jamais!
6.6 CARACTERÍSTICAS DAS IMPLEMENTAÇÕES
As seções a seguir apresentam diferentes aspectos na implementação de funções de agregação e
construções correlatas.
ROLL UP E CUBE
O Oracle e o SQL Server dispõem de dois construtores, rollup e cube, que podem ser utilizados
em conjunto com as funções de agregação. Para ilustrar seus efeitos, vamos considerar a
consulta
select c.curso, d.disciplina,
avg (i.nota) as nota_media,
count (i.matricula) as numero_inscricoes,
count (i.nota) as numero_notas
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
group by c.curso, d.disciplina
order by c.curso, d.disciplina
que produz
X
14 Capítulo 6: Somando, contando e agrupando
curso disciplina nota_media numero_inscricoes numero_notas
Direito Dir. Constitucional NULL 2 0
Direito Direito Civil 7,0 1 1
Direito Português 8,0 1 1
Informática Compiladores 9,5 1 1
Jornalismo Estatística 4,2 1 1
Jornalismo Português 5,5 1 1
Jornalismo Sociologia 6,5 2 2
Nesta formulação, as funções de agregação são aplicadas sobre os grupos de registros formados
por cursos e disciplinas. A mesma consulta, com a cláusula rollup,
select c.curso, d.disciplina,
avg (i.nota) as nota_media,
count (i.matricula) as numero_inscricoes,
count (i.nota) as numero_notas
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
group by c.curso, d.disciplina with rollup
order by c.curso, d.disciplina
select c.curso, d.disciplina,
avg (i.nota) as nota_media,
count (i.matricula) as numero_inscricoes,
count (i.nota) as numero_notas
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
group by rollup (c.curso, d.disciplina)
order by c.curso, d.disciplina
produz resultado semelhante, porém computadas também as funções de agregação para cada
grupo de disciplinas e para o conjunto de registros. Oracle e SQL Server apresentam leves
diferenças sintáticas na formulação das consultas.
curso disciplina nota_media numero_inscricoes numero_notas
NULL NULL 6.74286 9 7
Direito NULL 7.5 4 2
Direito Dir. Constitucional NULL 2 0
Direito Direito Civil 7.0 1 1
Direito Português 8.0 1 1
Informática NULL 9.5 1 1
Informática Compiladores 9.5 1 1
Jornalismo NULL 5.67542 4 4
Jornalismo Estatística 4.2 1 1
Jornalismo Português 5.5 1 1
Jornalismo Sociologia 6.5 2 2
O efeito da opção rollup é o de introduzir totais e subtotais no resultado da consulta. De fato,
foram incluídos mais dois níveis de agregação na consulta: por curso e geral. A nota média, o
número de inscrições e o número de notas são calculados também, para todas as disciplinas de
cada curso e para todas as discplinas de todos os cursos. Para o curso de Jornalismo, a média
geral das notas é 5,67542. Este nível de agregação não aparece na consulta de agregação sem o
Agregações para
todos os cursos e
todas as disciplinas
Agregações por
curso/disciplina
Agregações por
curso para todas
as disciplinas
Error! Style not defined. 15
construtor rollup. Assim, na coluna disciplina, o significado do valor nulo seria Todas e, na
coluna curso, o significado do valor seria também Todos.
Com a cláusula cube,
select c.curso, d.disciplina,
avg (i.nota) as nota_media,
count (i.matricula) as numero_inscricoes,
count (i.nota) as numero_notas
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
group by c.curso, d.disciplina with cube
order by c.curso, d.disciplina
select c.curso, d.disciplina,
avg (i.nota) as nota_media,
count (i.matricula) as numero_inscricoes,
count (i.nota) as numero_notas
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
group by cube (c.curso, d.disciplina)
order by c.curso, d.disciplina
o resultado é semelhante, porém acrescido dos totais gerais para cada disciplina. Cada uma das
linhas onde uma disciplina aparece como valor nulo na coluna curso deve ser lida como os
valores agregados daquela disciplina para todos os cursos.
curso disciplina nota_media numero_inscricoes numero_notas
NULL NULL 6,743 9 7
NULL Compiladores 9,5 1 1
NULL Dir. Constitucional NULL 2 0
NULL Direito Civil 7,0 1 1
NULL Estatística 4,2 1 1
NULL Português 6,75 2 2
NULL Sociologia 6,5 2 2
Direito NULL 7,5 4 2
Direito Dir. Constitucional NULL 2 0
Direito Direito Civil 7,0 1 1
Direito Português 8,0 1 1
Informática NULL 9,5 1 1
Informática Compiladores 9,5 1 1
Jornalismo NULL 5,675 4 4
Jornalismo Estatística 4,2 1 1
Jornalismo Português 5,5 1 1
Jornalismo Sociologia 6,5 2 2
É possível substituir o valor nulo, que aparece nas totalizações, por literais mais adequados.
Isso pode ser feito com o emprego da função de agregação grouping. Cada vez que uma linha é
adicionada ao resultado, a função grouping indica se a mesma é provocada pelo efeito das
opções de totalização e, com isso, pode-se usar valores diferentes de nulo no resultado. O
resultado da consulta
select c.curso, d.disciplina,
avg (i.nota) as nota_media,
count (i.matricula) as numero_inscricoes,
count (i.nota) as numero_notas,
grouping (c.curso) as grouping_curso,
grouping (d.disciplina) as grouping_disciplina
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
Agregações
por disciplina
para todos os
cursos
16 Capítulo 6: Somando, contando e agrupando
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
group by c.curso, d.disciplina with rollup
order by c.curso, d.disciplina
select c.curso, d.disciplina,
avg (i.nota) as nota_media,
count (i.matricula) as numero_inscricoes,
count (i.nota) as numero_notas,
grouping (c.curso) as grouping_curso,
grouping (d.disciplina) as grouping_disciplina
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
group by rollup (c.curso, d.disciplina)
order by c.curso, d.disciplina
mostra esse efeito.
curso disciplina nota_media numero_inscricoes numero_notas grouping_curso grouping_disciplina
NULL NULL 6,743 9 7 1 1
Direito NULL 7,5 4 2 0 1
Direito Dir. Constitucional NULL 2 0 0 0
Direito Direito Civil 7,0 1 1 0 0
Direito Português 8,0 1 1 0 0
Informática NULL 9,5 1 1 0 1
Informática Compiladores 9,5 1 1 0 0
Jornalismo NULL 5,675 4 4 0 1
Jornalismo Estatística 4,2 1 1 0 0
Jornalismo Português 5,5 1 1 0 0
Jornalismo Sociologia 6,5 2 2 0 0
Note que, cada vez que as colunas curso ou disciplina são totalizadas, a coluna de grouping
correspondente assume o valor 1. Esses valores podem ser utilizados para transformar o
conteúdo daquelas colunas, de modo a tornar mais compreensível o resultado.
select case when grouping (c.curso)=1 then 'Todos cursos'
else c.curso end as curso,
case when grouping (d.disciplina)=1 then 'Todas disciplinas'
else d.disciplina end as disciplina,
avg (i.nota) as nota_media,
count (i.matricula) as numero_inscricoes,
count (i.nota) as numero_notas
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
group by c.curso, d.disciplina with rollup
order by c.curso, d.disciplina
select decode ( grouping (c.curso), 1, 'Todos cursos', c.curso) as curso,
decode ( grouping (d.disciplina), 1, 'Todas disciplinas', d.disciplina) as disciplina,
avg (i.nota) as nota_media,
count (i.matricula) as numero_inscricoes,
count (i.nota) as numero_notas
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
group by rollup (c.curso, d.disciplina)
order by c.curso, d.disciplina
curso disciplina nota_media numero_inscricoes numero_notas
Error! Style not defined. 17
Direito Dir. Constitucional NULL 2 0
Direito Direito Civil 7,0 1 1
Direito Português 8,0 1 1
Direito Todas disciplinas 7,5 4 2
Informática Compiladores 9,5 1 1
Informática Todas disciplinas 9,5 1 1
Jornalismo Estatística 4,2 1 1
Jornalismo Português 5,5 1 1
Jornalismo Sociologia 6,5 2 2
Jornalismo Todas disciplinas 5,675 4 4
Todos cursos Todas disciplinas 6,743 9 7
Agora, nas linhas que totalizam os cursos aparecem os dizeres “Todas disciplinas” e nas linhas
que totalizam as disciplinas, “Todos cursos”.
A CLÁUSULA COMPUTE NO SQL SERVER
A cláusula compute no SQL Server oferece a possibilidade de extrair totais de controle para
consultas. Considere o seguinte exemplo.
select c.curso, d.disciplina,i.nota
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
order by c.curso, d.disciplina
Esta consulta produz uma lista de cursos, disciplinas e notas, como mostrado abaixo.
curso disciplina nota
Direito Dir. Constitucional NULL
Direito Dir. Constitucional NULL
Direito Direito Civil 7,0
Direito Português 8,0
Informática Compiladores 9,5
Jornalismo Estatística 4,2
Jornalismo Português 5,5
Jornalismo Sociologia 10,0
Jornalismo Sociologia 3,0
Suponha que seja interessante conhecer dados adicionais, como os valores de média, máximo e
mínimo das notas. A cláusula compute pode ser empregada neste caso.
select c.curso, d.disciplina,i.nota
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
order by c.curso, d.disciplina
compute avg (i.nota), max(i.nota), min(i.nota)
O resultado de uma consulta com essa é um pouco diferente dos demais vistos até aqui, uma vez
que são produzidos mais de um conjunto de registros no resultado (recordsets), como aparece a
seguir.
curso disciplina nota
Direito Dir. Constitucional NULL
Direito Dir. Constitucional NULL
Direito Direito Civil 7,0
Direito Português 8,0
Informática Compiladores 9,5
Jornalismo Estatística 4,2
Jornalismo Português 5,5
Jornalismo Sociologia 10,0
18 Capítulo 6: Somando, contando e agrupando
Jornalismo Sociologia 3,0
avg max min
6,74 10,0 3,0
Considere agora que essa estatísticas devem ser fornecidas para cada um dos cursos. Neste
caso, pode-se empregar a cláusula compute by, que tem função semelhante à que foi
apresentada acima.
select c.curso, d.disciplina,i.nota
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
order by c.curso, d.disciplina
compute avg (i.nota), max(i.nota), min(i.nota) by c.curso
O resultado agora compõe-se de seis recordsets, cada par referente a um dos três diferentes
cursos.
curso disciplina nota
Direito Dir. Constitucional NULL
Direito Dir. Constitucional NULL
Direito Direito Civil 7,0
Direito Português 8,0
avg max min
7,5 8,0 7,0
curso disciplina nota
Informática Compiladores 9,5
avg max min
9,5 9,5 9,5
curso disciplina nota
Jornalismo Estatística 4,2
Jornalismo Português 5,5
Jornalismo Sociologia 10,0
Jornalismo Sociologia 3,0
avg max min
5,67 10,0 3,0
Uma variação adicional poderia, ainda, ser escrita como
select c.curso, d.disciplina,i.nota
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
order by c.curso, d.disciplina
compute avg (i.nota), max(i.nota), min(i.nota) by c.curso
compute avg (i.nota), max(i.nota), min(i.nota)
Neste caso, seria produzido o resultado acima acrescentado das estatísticas globais, isto é, paratodos os registros de todos os cursos. No total, haveria sete recordsets.
A cláusula compute é mantida por questões de compatibilidade com versões anteriores do
SQL Server. Efeito semelhante pode ser obtido pelas cláusulas roll up ou até mesmo pela
Error! Style not defined. 19
união de consultas com e sem a cláusula group by.
FUNÇÕES DE AGREGAÇÃO NO ACCESS
As seguintes funções de agregação estão disponíveis no Access:
Função Nome em português2 Comentários
avg Média Calcula a média aritmética dos valores. Não inclui valores nulos no cálculo.
count Contagem Contagem de valores. Valores nulos não são contados a menos que o caracter
* (asterisco) seja usado como parâmetro.
count_big Contagem Idêntica à função count, exceto que retorna valores do tipo bigint.
min Mín Retorna o mínimo dentre os valores. Ignora valores nulos.
max Máx Retorna o máximo dentre os valores. Ignora valores nulos.
stdev Desv Calcula o desvio padrão dos valores. Retorna nulo se houver menos de dois
valores.
stdevp DesvP Calcula o desvio padrão de uma amostra dos valores. Retorna nulo se houver
menos de um valor.
sum Soma Calcula o somatório dos valores. Valores nulos são ignorados.
var Var Calcula a variância dos valores. Retorna nulo se houver menos de dois
valores.
varp VarP Calcula a variância de uma amostra dos valores. Retorna nulo se houver
menos de dois valores.
A cláusula pivot e a instrução transform, no Access, permitem a construção de consultas que
produzem resultados agregados bastante interessantes. A idéia básica é transformar os valores
de um determinado atributo, ou expressão, em colunas e, a partir daí, aplicar as funções de
agregação sobre os valores desejados. A consulta
transform avg (i.nota) as nota_media
select c.curso
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
group by c.curso
order by c.curso
pivot d.disciplina
produz
curso Compiladores Dir_ Constitucional Direito Civil Estatística Português Sociologia
Direito 7,0 8,0
Informática 9,5
Jornalismo 4,2 5,5 6,5
O resultado é uma tabela de referência cruzada. O atributo disciplina, da tabela DISCIPLINA, foi
declarado como pivô na última linha da consulta, fazendo com que os valores deste atributo
aparecessem como colunas no resultado final. Para cada combinação do pivô com o atributo
2 Na versão em Português, o Access traduz os nomes das funções de agregação quando as
apresenta na interface gráfica de consultas.
20 Capítulo 6: Somando, contando e agrupando
objeto da cláusula group by, são produzidos os resultados da função de agregação declarada na
instrução transform. Note que, para a disciplina Dir. Constitucional, o nome foi levemente
alterado porque o caracter “.” não é permitido num nome de coluna.
O mesmo exemplo, porém com as linhas e colunas invertidas, pode ser obtido pela consulta
transform avg (i.nota) as nota_media
select d.disciplina
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
group by d.disciplina
order by c.curso
pivot c.curso
que produz
disciplina Direito Informática Jornalismo
Compiladores 9,5
Dir. Constitucional
Direito Civil 7,0
Estatística 4,2
Português 8,0 5,5
Sociologia 6,5
A cláusula pivot, com a opção in, pode ser utilizada tanto para limitar os valores que aparecem
nas colunas como para determinar valores que devem aparecer sempre, mesmo que não constem
do banco de dados. Na consulta abaixo, a cláusula pivot determina que devem aparecer sempre
os cursos Direito, Jornalismo e Física, embora este último não esteja presente na base de dados.
transform avg (i.nota) as nota_media
select d.disciplina
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
group by d.disciplina
order by c.curso
pivot c.curso in ('Direito','Jornalismo','Física')
Todos os valores para o curso de Física são, previsivelmente, nulos, mas a coluna
obrigatoriamente aparece.
disciplina Direito Jornalismo Física
Compiladores
Dir. Constitucional
Direito Civil 7,0
Estatística 4,2
Português 8,0 5,5
Sociologia 6,5
Normalmente, a utilização da função transform deve ser acompanhada da cláusula group by.
Sendo assim, como é possível obter as agregações sobre algum atributo pivô sem ter que
vinculá-las a outro atributo? A consulta
transform avg (i.nota) as nota_media
select null as nulo
from aluno a, inscricao i, curso c, disciplina d
where a.matricula = i.matricula
and i.coddisciplina = d.coddisciplina
and a.codcurso = c.codcurso
group by null
order by c.curso
pivot c.curso
Error! Style not defined. 21
faz isto. Note que o valor nulo foi utilizado como referência, tanto na cláusula select como na
cláusula group by. O efeito final é mostrado a seguir.
Nulo Direito Informática Jornalismo
NULL 7,5 9,5 5,675
A obtenção de efeitos semelhantes é extremamente trabalhosa em SQL.
FUNÇÕES DE AGREGAÇÃO NO MYSQL
As seguintes funções de agregação estão disponíveis no MySQL:
Função Comentários
avg Calcula a média aritmética dos valores. Não inclui valores nulos no cálculo.
bit_or Retorna o resultado do operador lógico or sobre bits dos valores.
bit_and Retorna o resultado do operador lógico and sobre bits dos valores.
count Contagem de valores. Valores nulos não são contados a menos que o caracter * (asterisco) seja
usado como parâmetro. Pode ser utilizada em combinação com a opção distinct.
min Retorna o mínimo dentre os valores. Ignora valores nulos.
max Retorna o máximo dentre os valores. Ignora valores nulos.
sdt, stddev Calcula o desvio padrão dos valores. Os dois nomes são sinônimos.
sum Calcula o somatório dos valores. Valores nulos são ignorados.
variance Calcula a variância dos valores. Retorna nulo se houver menos de dois valores.
Na função agregada count é possível utilizar mais de uma coluna como parâmetro, na forma
count (c1, c2,...). Neste caso, a contagem retorna o número de combinações não nulas nas
colunas referenciadas. Nas demais implementações, efeito semelhante pode ser obtido pela
concatenação das diferentes colunas.
As funções agregadas bit_or e bit_and atuam sobre os bits dos valores. Cada valor é convertido
para um inteiro de 64 bits e é então operado. Tipos que não admitem conversão para inteiros
não são aceitos como operandos. A consulta
select bit_or (chst) as disjuncao,
bit_and (chst) as conjuncao
from disciplina
resulta em
disjuncao conjuncao
7 0
porque 4|4|2|2|3|3|4 = 7 e 4&4&2&2&3&3&4 = 0. Os caracteres | e & representam os
operadores or e and sobre bits.
Em alguns casos, MySQL permite que seja omitida a referência a algum item da cláusula group
by. Observe a consulta
select d.disciplina,
p.nome,
avg (i.nota) as nota_media
from disciplina d, professor p, inscricao i
22 Capítulo 6: Somando, contando e agrupando
where i.coddisciplina = d.coddisciplina
and d.codprofessor = p.codprofessor
group by d.disciplina
order by 1
e seu resultado
disciplina nome nota_media
Compiladores Lenira Rocha 9,5
Dir. Constitucional Zenubio Siqueira NULL
Direito Civil Olivia Straw 7
Estatística Pedro Amarante 4,2
Português Carlos Azambuja 6,75
Sociologia Silvia Ferreira 6,5
Normalmente, esta consulta provocaria um erro de compilação, uma vez que a expressão
p.nome, incluída na lista-alvo, não é mencionada na cláusula group by. Entretanto, o MySQL
permite essa construção desde que cada valor da expressão não mencionada na cláusula group
by possua um único valor para cada grupo que o envolve.No caso, o nome do professor é único
dentro do grupo de uma disciplina.
No MySQL é possível especificar a ordem em que os grupos são apresentados numa consulta
com a cláusula group by. A consulta
select d.disciplina,
avg (i.nota) as nota_media
from disciplina d, inscricao i
where i.coddisciplina = d.coddisciplina
group by d.disciplina desc
produz um resultado com os nomes das disciplinas dispostos em ordem decrescente.
disciplina nota_media
Sociologia 6,5
Português 6,75
Estatística 4,2
Direito Civil 7
Dir. Constitucional NULL
Compiladores 9,5
A cláusula group by pode combinar seus operandos com as palavras-chave asc e desc.
FUNÇÕES DE AGREGAÇÃO NO ORACLE
As seguintes funções de agregação estão disponíveis no Oracle:
Função Comentários
avg Calcula a média aritmética dos valores. Não inclui valores nulos no cálculo. Pode ser utilizada em
combinação com a opção distinct.
count Contagem de valores. Valores nulos não são contados a menos que o caracter * (asterisco) seja
usado como parâmetro. Pode ser utilizada em combinação com a opção distinct.
grouping Causa uma coluna adicional no resultado que é 1 quando uma linha está sendo adicionada pelo efeito
das cláusulas CUBE ou ROLLUP e 0 caso contrário.
min Retorna o mínimo dentre os valores. Ignora valores nulos. Pode ser utilizada em combinação com a
opção distinct.
max Retorna o máximo dentre os valores. Ignora valores nulos. Pode ser utilizada em combinação com a
Error! Style not defined. 23
opção distinct.
stddev Calcula o desvio padrão dos valores. Os dois nomes são sinônimos. Pode ser utilizada em
combinação com a opção distinct.
sum Calcula o somatório dos valores. Valores nulos são ignorados. Pode ser utilizada em combinação com
a opção distinct.
variance Calcula a variância dos valores. Retorna nulo se houver menos de dois valores. Pode ser utilizada em
combinação com a opção distinct.
O Oracle oferece algumas variações na escrita da cláusula group by, como mostra a sintaxe a
seguir.
<cláusula group by> :=
group by { <simple grouping clause> | <grouping sets clause> },... having <expressão lógica>
<simple grouping clause> :=
{ <expressão>
| ( <expressão>,... )
| { cube | rollup } ( { <expressão> | ( <expressão>,... ) },... )
| ( )
}
<grouping sets clause> :=
groupinp sets ( <simple grouping clause>,... )
A cláusula grouping sets permite que o programador defina as combinações de expressões que
devem ser agrupadas, evitando-se a formação de grupos que não são interessantes. Por
exemplo, considere a consulta a seguir, apresentada em exemplo anterior.
select d.disciplina,
count (*) as numero_de_inscricoes
from disciplina d, inscricao i
where d.coddisciplina = i.coddisciplina
group by d.disciplina
order by 1
Neste caso o grupamento é sempre o mesmo, ou seja, cada disciplina existente dentre as
inscrições formará um grupo de agregação. Com o auxílio de grouping sets, esta consulta
poderia reescrita como
select d.disciplina, i.matricula,
count (*) as numero_de_inscricoes
from disciplina d, inscricao i
where d.coddisciplina = i.coddisciplina
group by grouping sets ( d.disciplina, i.matricula )
order by 1 nulls first, 2
fazendo com que sejam agregados os valores com relação a mais de uma referência, como
mostra o seu resultado.
disciplina matricula numero_de_inscricoes
1001 2
1002 1
1005 2
1007 2
1010 2
Compiladores 1
Dir. Constitucional 2
Direito Civil 1
Estatística 1
Português 2
Sociologia 2
O mesmo resultado poderia ser obtido com a união de duas consultas de agregação simples,
como no caso a seguir.
24 Capítulo 6: Somando, contando e agrupando
select d.disciplina, null as matricula,
count (*) as numero_de_inscricoes
from disciplina d, inscricao i
where d.coddisciplina = i.coddisciplina
group by d.disciplina, null
union
select null as disciplina, i.matricula,
count (*) as numero_de_inscricoes
from disciplina d, inscricao i
where d.coddisciplina = i.coddisciplina
group by null, i.matricula
order by 1 nulls first, 2
∴
Em certas situações, o Oracle oferece uma possibilidade interessante que é a de aplicar uma
função de agregação sobre o resultado de outra função de agregação. Parece complicado, mas
os exemplos a seguir ilustram essa possibilidade.
A consulta
select avg (nota) as media_por_aluno
from inscricao
group by matricula
retorna a lista de médias de notas dos alunos, separadas pelas matrículas dos alunos. Ou seja,
cada média da lista é a média das notas de um aluno, como é mostrado a seguir.
media_por_aluno
8
9,5
3,6
7
7,75
É possível aplicar uma outra função de agregação sobre este resultado. Por exemplo, para
retornar a maior média entre todas, pode-se escrever
select max (avg (nota)) as media_maxima
from inscricao
group by matricula
com o seguinte resultado
media_maxima
9,5
Outras funções de agregação podem ser utilizadas. Para obtermos a média das médias, bastaria
construir uma consulta como
select avg (avg (nota)) as media_das_medias
from inscricao
group by matricula
produzindo
media_das_medias
6,75
Note que a construção da consulta é toda voltada para a função de agregação mais interna,
sendo que a função mais externa trabalha no resultado consolidado da primeira função. Esse
conceito pode ser ilustrado com uma consulta que conta o número de médias produzidos.
select count (avg (nota))
Error! Style not defined. 25
from inscricao
group by matricula
O resultado é 5. Porém se incluirmos uma cláusula having nessa mesma consulta, filtrando as
médias maiores que 4, como na consulta
select count (avg (nota)) as contagem_de_medias
from inscricao
group by matricula
having avg (nota) > 4
o resultado passa a ser
contagem_de_medias
4
Note que o filtro ocasionado pela cláusula having aplica-se na formação das médias, e não na
contagem final. Referências à função count na cláusula having, neste caso, não são aceitas,
provocando erros de sintaxe. Uma consulta como
select count (avg (nota)) as contagem_de_medias
from inscricao
group by matricula
having count(avg (nota)) > 4
não pode ser processada pelo Oracle.
FUNÇÕES DE AGREGAÇÃO NO SQL SERVER
As seguintes funções de agregação estão disponíveis no SQL Server:
Função Comentários
avg Calcula a média aritmética dos valores. Não inclui valores nulos no cálculo. Pode ser utilizada em
combinação com a opção distinct.
count Contagem de valores. Valores nulos não são contados a menos que o caracter * (asterisco) seja
usado como parâmetro. Pode ser utilizada em combinação com a opção distinct.
grouping Causa uma coluna adicional no resultado que é 1 quando uma linha está sendo adicionada pelo efeito
das cláusulas CUBE ou ROLLUP e 0 caso contrário.
max Retorna o mínimo dentre os valores. Ignora valores nulos.
min Retorna o máximo dentre os valores. Ignora valores nulos.
stdev Calcula o desvio padrão dos valores. Retorna nulo se houver menos de dois valores.
stdevp Calcula o desvio padrão de uma amostra dos valores. Retorna nulo se houver menos de um valor.
sum Calcula o somatório dos valores. Valores nulos são ignorados. Pode ser utilizada em combinação com
a opção distinct.
var Calcula a variância dos valores. Retorna nulo se houver menos de dois valores.
varp Calcula a variância de uma amostra dos valores. Retorna nulo se houver menos de dois valores.
A opção distinct somente pode ser utilizada sobre colunas simples, sem expressões.
X
26 Capítulo 6: Somando, contando e agrupando
EXERCÍCIOS
6.1 Escreva uma consulta SQL que produza a lista dos três alunos com as melhores médias, em
ordem decrescente, produzindo o resultado abaixo.
aluno media
Maria Rita Colatti 9,5
Ricardo Biondi 8
Maria Lucia Silva 7,75
6.2 Escreva uma consulta SQL que forneça, paracada professor, a menor nota, a maior nota, a
média e o desvio padrão das notas. O resultado esperado, em ordem alfabética, é mostrado a
seguir.
nome menor_nota maior_nota media desvio_padrao
Carlos Azambuja 5,5 8 6,75 1,76776695296637
Lenira Rocha 9,5 9,5 9,5
Olivia Straw 7 7 7
Pedro Amarante 4,2 4,2 4,2
Silvia Ferreira 3 10 6,5 4,94974746830583
Zenubio Siqueira
6.3 Escreva uma consulta SQL, no Access, que produza uma tabela de referência cruzada
contendo o desempenho dos alunos de cada curso, por sexo. Utilize a média geral de cada
aluno como critério.
curso F M
Direito 7,5
Informática 9,5
Jornalismo 5,675
6.4 Considere novamente a instância da tabela ALUNO após a inclusão de homônimos, como foi
feito num exemplo do capítulo 4.
Matricula Nome Sexo CodCurso Nascimento
1001 Ricardo Biondi M DIR 21/02/80
1002 Maria Rita Colatti F INF 10/11/78
1004 Oscarito Vianna M DIR 14/08/79
1005 Barbara Carlito F JOR 29/10/79
1007 Carlos Maradona M DIR 30/06/77
1008 Sacadura Miranda M INF 12/12/81
1010 Maria Lucia Silva F JOR 10/08/75
1014 Maria Aparecida da Silva F JOR 24/09/77
1015 Oscarito Vianna M INF 12/05/79
1017 Maria Aparecida da Silva F INF 07/04/78
Escreva uma consulta SQL que indique, para cada nome que tenha algum homônimo, em
ordem alfabética, o número de ocorrências do mesmo, produzindo o resultado abaixo.
nome ocorrencias
Maria Aparecida da Silva 2
Oscarito Vianna 2
6.5 Escreva uma consulta SQL que forneça o número de possíveis inscrições na base de dados,
isto é, o número de combinações de alunos e disciplinas, como abaixo.
possiveis_inscricoes
49
6.6 Observe a tabela mostrada abaixo, que registra o resultado de um páreo no Jockey Clube.
Error! Style not defined. 27
Cavalo Tempo
Estupenda 159,25
Muddy River 160,42
Golden Champ 157,63
Easy Rider 159,70
Escreva uma consulta SQL que produza o ranking numerado desse páreo, apresentando os
cavalos em ordem de chegada.
ranking cavalo
1 Golden Champ
2 Estupenda
3 Easy Rider
4 Muddy River
Tente resolver o problema sem usar funções que retornem o número da linha diretamente,
como faz a função rownum, no Oracle.
6.7 Considere novamente o banco de dados do exercício 5.5 e escreva uma consulta SQL que
produza o número de carros que estão equipados com cada um dos diversos acessórios. O
resultado deve ser o que é mostrado a seguir.
acessorio numero_de_carros
Câmbio automático 1
CD-Player 4
Direção hidráulica 4
Rodas de liga leve 4
Vidros elétricos 4
6.8 Indique o resultado para cada uma das consultas abaixo, nas implementações que as
permitem. Tente resolver este exercício sem auxílio do computador e explique os resultados.
a.
select sum (1) as x
from aluno
b.
select sum (1) as x
from aluno, inscricao, disciplina
c.
select sum (1) as x
from ( aluno a inner join inscricao i on a.matricula = i.matricula )
inner join disciplina d on i.coddisciplina = d.coddisciplina
6.9 Considere as duas tabelas RESULTADO e JOGO, mostradas abaixo. A primeira contém os
primeiros 500 resultados da Megasena, onde a cada concurso são sorteadas 6 dezenas, que
podem variar entre 1 e 60; a segunda contém entre 6 e 10 palpites de dezenas.
Resultado
Concurso Dezena
1 4
1 5
1 30
1 33
1 41
1 52
... ...
500 21
500 29
500 35
500 36
500 38
500 54
Jogo
Dezena
12
17
26
27
52
53
28 Capítulo 6: Somando, contando e agrupando
Escreva consultas SQL que produzam:
a. o ranking numerado das dezenas, contendo o número de concursos em que cada uma das
60 dezenas forma sorteadas;
Ordem Dezena Número de concursos
1 ... ...
2 ... ...
... ... ...
60 ... ...
b. para um jogo qualquer, quantos acertos teriam ocorrido para cada um dos concursos
havidos. Mostre apenas os concursos onde houve pelo menos um acerto.
Concurso Número de acertos
... ...
... ...
c. repetir o item (b), porém incluindo os concursos para os quais houve zero acertos.
6.10 Como calcular e média de um conjunto de valores sem o auxílio das funções de
agregação? Considerando a tabela Páreo, do exemplo 6.6, escreva uma consulta que calcule o
tempo médio dos cavalos sem utilizar funções de agregação. Note que a solução deverá ser
específica para instância mostrada, não sendo genericamente aplicável se, por exemplo, o
número de cavalos tiver aumentado ou diminuído.