Escolhendo linhas com a cláusula HAVING

A cláusula HAVING define condições na cláusula GROUP BY semelhante ao modo que WHERE interage com SELECT. O critério de pesquisa WHERE é aplicado antes da execução da operação de agrupamento; o critério de pesquisa HAVING é aplicado depois que a operação de agrupamento é executada. A sintaxe HAVING é semelhante à sintaxe WHERE, exceto se HAVING contiver funções de agregação. Cláusulas HAVING podem consultar quaisquer dos itens que aparecem na lista de seleção.

O exemplo seguinte agrupa a tabela SalesOrderDetail por intermédio da ID de produto e somente inclui os grupos de produtos que têm ordens totalizando mais de R$1.000.000 e cujas quantidades médias de ordem são inferiores a 3.

USE AdventureWorks2008R2;
GO
SELECT ProductID, AVG(OrderQty) AS AverageQuantity, SUM(LineTotal) AS Total
FROM Sales.SalesOrderDetail
GROUP BY ProductID
HAVING SUM(LineTotal) > $1000000.00
AND AVG(OrderQty) < 3 ;
GO

Observe que quando várias condições são incluídas em HAVING, são combinadas com AND, OR ou NOT.

Para ver os produtos com vendas totais maiores que R$2.000.000, use a seguinte consulta:

USE AdventureWorks2008R2;
GO
SELECT ProductID, Total = SUM(LineTotal)
FROM Sales.SalesOrderDetail
GROUP BY ProductID
HAVING SUM(LineTotal) > $2000000.00 ;
GO

Aqui está o conjunto de resultados.

ProductID   Total
----------- ----------------------
781         3864606.54937208
969         2010943.97244001
793         2897478.01200001
784         3699803.72383008
780         3880441.60780208
976         2079038.42948
795         2268057.09000002
783         4548164.01783709
779         4170215.3849281
782         5032968.13026809
794         2679200.01336002
753         2006264.4236

(12 row(s) affected)

Se você desejar verificar se existem pelo menos mil e quinhentos itens envolvidos nos cálculos para cada produto, use HAVING COUNT (*) > 1500 para eliminar os produtos que retornam totais para menos do que 1500 itens vendidos. A consulta parece com:

USE AdventureWorks2008R2;
GO
SELECT ProductID, SUM(LineTotal) AS Total
FROM Sales.SalesOrderDetail
GROUP BY ProductID
HAVING COUNT(*) > 1500 ;
GO

Compreendendo a sequência correta onde as cláusulas WHERE, GROUP BY e HAVING são aplicadas, ajuda a codificar consultas eficientes:

  • A cláusula WHERE é usada para filtrar as linhas que resultam das operações especificadas na cláusula FROM.

  • A cláusula GROUP BY é usada para agrupar o resultado da cláusula WHERE.

  • A cláusula HAVING é usada para filtrar linhas do resultado agrupado.

Para qualquer critério de pesquisa que poderia ser aplicado antes ou depois da operação de agrupamento, é mais eficiente especificá-los na cláusula WHERE. Isto reduz o número de linhas que precisam ser agrupadas. Os únicos critérios de pesquisa que deveriam ser especificados na cláusula HAVING são os critérios de pesquisa que devem ser aplicados depois da operação de agrupamento ser executada.

O otimizador de consulta do Microsoft SQL Server pode lidar com a maioria destes critérios. Se o otimizador de consulta determinar que um critério de pesquisa HAVING pode ser aplicado antes da operação de agrupamento ser executada, isso será feito. É possível que o otimizador de consulta não consiga reconhecer todos os critérios de pesquisa HAVING que podem ser aplicados depois antes da operação de agrupamento. É recomendado que você coloque todos os critérios de pesquisa na cláusula WHERE em vez de na cláusula HAVING.

Este exemplo mostra uma cláusula HAVING com uma função de agregação. Ela agrupa as linhas na tabela SalesOrderDetail por ID de Produto e elimina os produtos cujas quantidades médias de ordem forem cinco ou menos.

USE AdventureWorks2008R2;
GO
SELECT ProductID 
FROM Sales.SalesOrderDetail
GROUP BY ProductID
HAVING AVG(OrderQty) > 5
ORDER BY ProductID ;
GO

O exemplo a seguir mostra uma cláusula HAVING sem uma função de agregação. Ela agrupa as linhas na tabela ProductModel por nome e elimina os nomes que não iniciam com Montanha.

USE AdventureWorks2008R2;
GO
SELECT pm.Name, AVG(ListPrice) AS 'Average List Price'
FROM Production.Product AS p
JOIN Production.ProductModel AS pm
ON p.ProductModelID = pm.ProductModelID
GROUP BY pm.Name
HAVING pm.Name LIKE 'Mountain%'
ORDER BY pm.Name ;
GO

Observe que a cláusula ORDER BY pode ser usada para ordenar o resultado de uma cláusula GROUP BY.

Consulte também

Referência