Elegir filas con la cláusula HAVING

La cláusula HAVING establece las condiciones de la cláusula GROUP BY de la misma forma que WHERE interactúa con SELECT. Mientras que las condiciones de búsqueda de WHERE se aplican antes de que se produzca la operación de agrupamiento, las condiciones de búsqueda de HAVING se aplican después. La sintaxis de la cláusula HAVING es similar a la de la cláusula WHERE, con la diferencia de que HAVING puede contener funciones de agregado. Las cláusulas HAVING pueden hacer referencia a cualquiera de los elementos que aparecen en la lista de selección.

En el siguiente ejemplo se agrupa la tabla SalesOrderDetail por Id. de producto y sólo se incluyen los grupos de productos con pedidos cuyo total sea superior a 1.000.000 dólares y cuyas cantidades de pedido promedio sean 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 cuando en HAVING se incluyen varias condiciones, éstas se combinan mediante AND, OR o NOT.

Para ver los productos con ventas totales superiores a 2.000.000 de dólares, utilice la siguiente consulta:

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

El conjunto de resultados es el siguiente.

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)

Si desea comprobar que hay al menos 1.500 elementos implicados en los cálculos de cada producto, utilice HAVING COUNT(*) > 1500 para eliminar los productos que devuelvan totales inferiores a 1.500 elementos vendidos. La consulta sería la siguiente:

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

La comprensión de la secuencia correcta en la que se aplican las cláusulas WHERE, GROUP BY y HAVING ayuda a codificar consultas eficaces:

  • La cláusula WHERE se utiliza para filtrar las filas que resultan de las operaciones especificadas en la cláusula FROM.

  • La cláusula GROUP BY se usa para agrupar el resultado de la cláusula WHERE.

  • La cláusula HAVING se usa para filtrar las filas del resultado agrupado.

Es más eficaz especificar en la cláusula WHERE las condiciones de búsqueda que se pueden establecer antes o después de la operación de agrupamiento. Esto reduce el número de filas que tienen que agruparse. Las únicas condiciones de búsqueda que se deben especificar en la cláusula HAVING son aquellas que se deben aplicar una vez que se hayan realizado las operaciones de agrupamiento.

El optimizador de consultas de Microsoft SQL Server puede tratar la mayor parte de estas condiciones. Si el optimizador determina que una condición de búsqueda HAVING se puede aplicar antes de la operación de agrupamiento, lo hará. Puede que el optimizador no reconozca todas las condiciones de búsqueda de HAVING que se pueden aplicar antes de la operación de agrupamiento. Se recomienda colocar todas estas condiciones de búsqueda en la cláusula WHERE en lugar de en la cláusula HAVING.

En este ejemplo se muestra una cláusula HAVING con una función de agregado. Se agrupan las filas de la tabla SalesOrderDetail por Id. de producto y se eliminan los productos cuyas cantidades de pedido promedio sean cinco o menos.

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

En el siguiente ejemplo se muestra una cláusula HAVING sin funciones de agregado. Se agrupan las filas de la tabla ProductModel por nombre y se eliminan los nombres que no empiecen por la palabra Mountain.

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 se puede utilizar la cláusula ORDER BY para ordenar el resultado de una cláusula GROUP BY.

Vea también

Referencia