MC536 SQL - Funções agregado

Atualizado em 2 Nov 22 - Prof. Célio Guimarães

Este documento é um resumo do material apresentado nas pp 135-144 do livro texto e será apresentado através de exemplos.
Funções agregado permitem obter estatísticas numéricas sobre atributos numéricos de uma ou mais tabelas. Elas tomam como argumento o nome de um atributo ou o caracter "*" e são as seguintes: min(), max(), sum(), avg(), count() ou count(*); avg significa "média"

  1. Uso na clausula select do comando select. Exemplo: select max(premio), min(premio),avg(premio),sum(premio), count(*) from T -- opcionalmente where condição Obs: count(*) retorna o número de linhas na tabela T

  2. cláusula group by do comando select. Como o nome indica, ela agrupa as linhas da tabela em grupos, onde cada grupo contém as linhas do(s) atributo(s) mencionado(s} após group by. Ela deve aparecer após a cláusula from (ou, possivelmente, após a clausula where). Exemplo: select numj, count(*) from T group by numj Ponto importante: o comando select retorna uma única linha para cada grupo usualmente com um ou mais resultados de funções agregado. No exemplo acima ele retorna para cada jogador, o número de linhas do seu grupo e que neste caso representa o número de torneios que ele venceu ou seja, é a solução da consulta: para cada jogador mostre o número de torneios que ele venceu". Se agora quisermos: "para cada jogador e para cada torneio que ele venceu mostre o número de torneios vencidos", teríamos: select numj,numt, count(*) from T group by numj, numt ou seja, cada grupo agora é formado por "um jogador e um torneio" e, portanto, o número de linhas do grupo representa a quantidade de vezes que ele venceu esse torneio (em anos distintos). Se na 2a consulta acima quisermos o nome do jogador teríamos: select nome, count(*) from J,T where J.numj = T.numj group by nome order by count(*) desc
  3. cláusula having Se quisermos impor uma condição adicional para que linhas sejam excluídas do grupo, usaremos having condição, onde condição é uma expressão booleana incluindo uma função agregado e/ou um atributo da cláusula group by. Esta clausula funciona como uma "seleção de linhas dentro do grupo" assim como a clausula where funciona como uma "seleção de linhas de toda a tabela" Exemplo: "Dê o número de cada jogador que venceu mais de um torneio". select numj from T group by numj having count(*) >1 Se quisermos saber também "quantos torneios ele venceu" basta acrescentar count(*) na cláusula select A seguinte variante da consulta acima é não trivial: "dê o número dos jogadores que venceram pelo menos dois torneios distintos: select numj from T group by numj having count(distinct numt) >1 Esta consulta não retorna 910 (Gustavo Kuerten) que venceu 3 vezes o torneio 2 (Roland Garros), mas nenhum outro torneio.

    Roteiro prático para obter soluções de consultas envolvendo group by

    • descubra a(s) tabela(s) envolvida(s): cláusula from. Se mais de uma, faça a junção apropriada usando a cláusula where.
    • ache e introduza o(s) atributo(s) requerido(s) na cláusula group by
    • introduza na cláusula select os atributos e funções agregado requeridos pelo problema (são os mesmos da clausula group by). ache as funções agregado requeridas pelo problema e coloque-as também na cláusula select
    • se requerido pelo problema, introduza a cláusula having(condição)
    • se requerido ou apropriado, finalize o comando select com a cláusula order by

    Exercícios complementares

    -- q83 Para cada jogador que venceu mais de um torneio dê a soma total dos premios recebidos select numj, sum(premio), max(premio) from torneios group by numj having count(*) > 1 -- q84 quantos torneios foram vencidos no mesmo ano por cada jogador? select numj,ano, count(*) from torneios group by numj,ano order by count(*) desc, numj -- q87 "Encontre as cidades em que mais de dois jogadores vivem. -- q88 Dê o nome de cada jogador que venceu mais de um torneio mostre também o número de torneios por ele vencido. select nome, count(*) from J,T where J.numj = T.numj group by nome having count(*) > 1 -- q90 Para cada cidade de residência dê o número de jogadores nascidos após 1975 select cid_res, count(*) from jogadores where ano_n > 75 group by cid_res -- q91 "Dê o nome e ano de nascimento de cada jogador nascido após o ano médio de nascimento de todos os jogadores -- q92 para cada jogador que ganhou algum premio acima de 400 KUS$ dê o seu número e o máximo premio qu ele ganhou -- q94 "Para cada jogador que venceu algum torneio, dê a porcentagem de torneios por ele vencidos, em relação ao número total de torneios -- q95 Obtenha a média do número de materiais fornecidos por cada fornecedor, -- isto é, "seu número de fornecimentos/número de fornecedores" select f,count(*)/(select count(*) from F) from FM group by f -- q97 Para cada jogador, dê o seu nome e o número de torneios distintos que ele venceu. -- q98 * "Dê o nome e numero de torneios distintos ganhos por cada jogador que venceu pelo menos 2 torneios distintos -- q99 ** "Dê o nome do jogador que venceu o maior número de torneios distintos e qual foi esse número. Sugestão: use o operador all seguido de subconsulta q999 "usando apenas a tabela FM(f,m) e recursos de contagem do SQL, obtenha os fornecedores que fornecem todos os materiais, exibindo também o número de materiais encontrado. Obs: você não pode supor que o número de materiais seja conhecido. -- q101 ** Para cada torneio, quais jogadores o venceram mais vezes e quantas vezes eles o venceram. Nota: esta solução usa uma consulta correlacionada select numt, numj,count(*) from torneios t group by t.numt,t.numj having count(*) >= all (select count(*) from torneios where numt=t.numt group by numj) order by t.numt -- q102 Dê o nome de cada torneio e os nomes dos jogadores que o venceram mais vezes. Obs: esta solução usando mysql, não segue o padrão SQL/92. select nome, nomet, count(*) from torneios t, J, LT where t.numj = J.numj and t.numt=LT.numt group by t.numt,t.numj having count(*) >= all (select count(*) from torneios where numt=t.numt group by numj) order by t.numt -- q103 Para cada ano, quais jogadores venceram o maior número de torneios nesse ano, e quantos torneios eles venceram? Considere a nossa tabela de "Pais e Filhos" D(p,f). Você deve eliminar repetições e, possivelmente, ordenar os resultados das consultas: -- q111a Para cada casal de avós, obtenha o número de seus netos. -- q111b Obtenha o(s) casai(s) p1,p2 que possuem o maior número de filhos. -- q115 Obtenha pares de pessoas que são meio-irmãos (isto é apenas um dos pais é comum a ambos). -- q116 Apresente uma lista numerada e em ordem alfabética com os nomes dos jogadores. Este curioso problema vem mostrar novamente o poder do produto cartesiano: vamos fazer uma autojunção da tabela de Jogadores consigo mesma, comparando a coluna nome consigo mesma e agrupando o resultado da comparação na coluna nome da 1a tabela: o número de linhas do grupo dá a numeração desejada. select count(*), j1.nome from J j1, J j2 where j1.nome >= j2.nome group by j1.nome order by count(*)