Como a maioria dos produtos de banco de dados relacional, o
PostgreSQL aceita
funções de agregação.
Uma função de agregação calcula 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
-----
23
(1 linha)
Se for desejado saber a cidade (ou cidades) onde essa 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, nesse 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 aqui por meio de uma
subconsulta:
SELECT cidade
FROM clima
WHERE temp_min = (SELECT max(temp_min) FROM clima);
cidade
---------------
Salvador - BA
(1 linha)
Isso está correto, porque a subconsulta é uma ação independente, que calcula sua agregação de forma isolada 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
---------------------+-----
Rio de Janeiro - RJ | 17
Salvador - BA | 23
(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) < 30;
cidade | max
---------------------+-----
Rio de Janeiro - RJ | 17
Salvador - BA | 23
(2 linhas)
mostrando os mesmos resultados, mas apenas para as cidades que
possuem todas as temperaturas mínimas abaixo de 30. 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) < 30;
cidade | max
---------------+-----
Salvador - BA | 23
(1 linha)
É importante compreender a interação entre a agregação e as cláusulas
WHERE e HAVING do
SQL. A diferença fundamental entre
WHERE e HAVING é essa:
WHERE seleciona linhas da entrada antes que os
grupos e agregações sejam formados (assim, controla quais linhas
vão para o cálculo da agregação), enquanto HAVING
seleciona as linhas de grupo após os grupos e agregações serem formados.
Assim, a cláusula WHERE não deve conter funções
de agregação; não faz sentido tentar usar uma agregação para determinar
quais linhas servirão de entrada para a agregação. Por outro lado, a
cláusula HAVING sempre contém funções de agregação.
(Estritamente falando, é permitido escrever uma cláusula
HAVING que não usa agregações, mas raramente é útil.
A mesma condição pode ser usada de forma mais eficiente na cláusula
WHERE.)
No exemplo anterior, podemos aplicar a restrição do nome da cidade
na cláusula WHERE, porque ela não precisa de
agregação. Isso é mais eficiente do que adicionar a restrição à
cláusula HAVING, porque evitamos fazer os cálculos
de agrupamento e agregação para todas as linhas que falhariam na
condição da cláusula WHERE.
Outra forma de selecionar as linhas que entram em um cálculo de
agregação é usar a cláusula FILTER,
que é uma opção para cada agregação:
SELECT cidade, count(*) FILTER (WHERE temp_min < 20), max(temp_min)
FROM clima
GROUP BY cidade;
cidade | count | max
---------------------+-------+-----
Rio de Janeiro - RJ | 1 | 17
Salvador - BA | 0 | 23
(2 linhas)
A cláusula FILTER é muito parecida com a cláusula
WHERE, com a diferença de que remove linhas apenas
da entrada da função de agregação específica à qual está associada.
Aqui, a agregação count conta apenas as linhas com
temp_min abaixo de 20; mas a agregação
max ainda é aplicada a todas as linhas, então
ainda encontra a leitura de 23.