Como a maioria dos produtos de banco de dados relacional, o PostgreSQL suporta funções de agregação. Uma função de agregação computa um único resultado para várias linhas de entrada. Por exemplo, existem funções de agregação para contar (count
), somar (sum
), calcular a média (avg
), o valor máximo (max
) e o valor mínimo (min
) para um conjunto de linhas.
Para servir de exemplo, é possível encontrar a maior temperatura mínima ocorrida em qualquer lugar usando
SELECT max(temp_min) FROM clima; max ----- 46 (1 linha)
Se for desejado saber a cidade (ou cidades) onde esta temperatura ocorreu pode-se tentar usar
SELECT cidade FROM clima WHERE temp_min = max(temp_min); ERRADO
mas não vai funcionar, porque a função de agregação max
não pode ser usada na cláusula WHERE (Esta restrição existe porque a cláusula WHERE determina as linhas que vão passar para o estágio de agregação e, portanto, precisa ser avaliada antes das funções de agregação serem computadas). Entretanto, como é geralmente o caso, a consulta pode ser reformulada para obter o resultado pretendido, o que será feito por meio de uma subconsulta:
SELECT cidade FROM clima WHERE temp_min = (SELECT max(temp_min) FROM clima); cidade --------------- São Francisco (1 linha)
Isto está correto porque a subconsulta é uma ação independente, que calcula sua agregação em separado do que está acontecendo na consulta externa.
As agregações também são muito úteis em combinação com a cláusula GROUP BY. Por exemplo, pode ser obtida a maior temperatura mínima observada em cada cidade usando
SELECT cidade, max(temp_min) FROM clima GROUP BY cidade; cidade | max -----------------+----- Hayward | 37 São Francisco | 46 (2 linhas)
produzindo uma linha de saída para cada cidade. Cada resultado da agregação é computado sobre as linhas da tabela correspondendo a uma cidade. As linhas agrupadas podem ser filtradas utilizando a cláusula HAVING
SELECT cidade, max(temp_min) FROM clima GROUP BY cidade HAVING max(temp_min) < 40; cidade | max -----------+----- Hayward | 37 (1 linha)
que mostra os mesmos resultados, mas apenas para as cidades que possuem todos os valores de temp_min abaixo de 40. Para concluir, se desejarmos somente as cidades com nome começando pela letra "S" podemos escrever:
SELECT cidade, max(temp_min) FROM clima WHERE cidade LIKE 'S%' (1) GROUP BY cidade HAVING max(temp_min) < 40;
É importante compreender a interação entre as agregações e as cláusulas WHERE e HAVING do SQL. A diferença fundamental entre WHERE e HAVING é esta: WHERE seleciona as linhas de entrada antes dos grupos e agregações serem computados (portanto, controla quais linhas irão para o computo da agregação), enquanto HAVING seleciona linhas de grupo após os grupos e agregações serem computados. Portanto, a cláusula WHERE não pode conter funções de agregação; não faz sentido tentar utilizar uma agregação para determinar quais linhas serão a entrada da agregação. Por outro lado, a cláusula HAVING sempre contém funções de agregação (A rigor, é permitido escrever uma cláusula HAVING que não possua agregação, mas é desperdício: A mesma condição poderia ser utilizada de forma mais eficiente no estágio do WHERE). [1]
No exemplo anterior, a restrição do nome da cidade pode ser aplicada na cláusula WHERE, porque não necessita de nenhuma agregação, sendo mais eficiente que colocar a restrição na cláusula HAVING, porque evita realizar os procedimentos de agrupamento e agregação em todas as linhas que não atendem a cláusula WHERE.
[1] |
Veja o Exemplo 7-1 (N. do T.) |