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 observada 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 quais linhas serão incluídas no cálculo da agregação e, neste caso, teria que 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 isoladamente 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 os valores de max(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 possui agregação, mas raramente é útil: A mesma condição pode ser utilizada de forma mais eficiente no estágio do WHERE).
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. É 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.