We saw earlier that a
WHERE
clause filter won't work with an aggregate function column. However, the HAVING
clause will filter a regular, or non-aggregate column. To do this, we must include the GROUP BY
clause. Have a look at this query:USE packt_online_shop;SELECT PC.ProductCategoryID, PC.ProductCategoryName,AVG(P.UnitKGWeight) AS 'AVERAGE PRODUCT KG WEIGHT',MIN(P.NetRetailPrice) AS 'MINIMUM NET RETAIL PRICE'Products P ON PC.ProductCategoryID = P.ProductCategoryIDFROM ProductCategories PC INNER JOINHAVING PC.ProductCategoryID = 2;
It did not work because we forgot the
GROUP BY
clause. If we add the GROUP BY
clause, it will work:USE packt_online_shop;SELECT PC.ProductCategoryID, PC.ProductCategoryName,AVG(P.UnitKGWeight) AS 'AVERAGE PRODUCT KG WEIGHT',MIN(P.NetRetailPrice) AS 'MINIMUM NET RETAIL PRICE'Products P ON PC.ProductCategoryID = P.ProductCategoryIDFROM ProductCategories PC INNER JOIN GROUP BY PC.ProductCategoryID, PC.ProductCategoryNameHAVING PC.ProductCategoryID = 2;
As we can see, the query worked.
If we set up the
GROUP BY
clause correctly, as explained previously, we can use both WHERE
and HAVING
clauses in the same query. This query combines WHERE
and HAVING
clauses in one query:USE packt_online_shop;SELECT OI.OrderID, P.ProductName,SUM((OI.UnitPrice - OI.Discount) * OI.Quantity) AS'TOTAL REVENUE PER PRODUCT PER ORDER'Products P ON OI.ProductID = P.ProductIDFROM OrderItems OI INNER JOIN WHERE OI.OrderID = 5HAVING SUM((OI.UnitPrice - OI.Discount) * OI.Quantity) > 4.50GROUP BY OI.OrderID, P.ProductNameORDER BY 'TOTAL REVENUE PER PRODUCT PER ORDER' ASC;
The line 5
SUM
aggregate function requires the line 8 GROUP BY
clause, and the line 9 HAVING
clause filters on the SUM
function values. The line 7 WHERE
clause filters the OrderItems.OrderID
values. As explained earlier, we could have placed all the filters in the HAVING
clause, combining them with the AND
keyword:
Look at the
HAVING
clause on line 9:
USE packt_online_shop;
SELECT OI.OrderID, P.ProductName,
SUM((OI.UnitPrice - OI.Discount) * OI.Quantity) AS
'TOTAL REVENUE PER PRODUCT PER ORDER'
Products P ON OI.ProductID = P.ProductID
FROM OrderItems OI INNER JOIN
WHERE OI.OrderID = 5
HAVING (SUM((OI.UnitPrice - OI.Discount) * OI.Quantity) > 4.50)
GROUP BY OI.OrderID, P.ProductName
AND (OI.OrderID = 5)
ORDER BY 'TOTAL REVENUE PER PRODUCT PER ORDER' DESC
However, we want as much filtering as possible to happen in the
WHERE
clause and as little as possible in the HAVING
clause. As MySQL runs a query, it evaluates the WHERE
clause early on. This eliminates as many rows as possible as early as possible. MySQL then runs the GROUP BY
clause if it sees one, and then runs the HAVING
clause if it sees one. If a query eliminates as many rows as possible with the WHERE
clause, MySQL can avoid operations on fewer rows. That way, it will use fewer resources and the query will have better performance.
0 Comments