You get a bonus - 1 coin for daily activity. Now you have 1 coin

Aggregate functions and nested aggregate functions or a subquery in the FROM clause, the difference between WHERE and HAVING, and query execution order

Practice



Aggregate functions

Aggregate functions (eng. aggregate function) — are mathematical functions, applied to a set of input data and returning a single resulting value for them.

Aggregate functions are used to summarize data in programming languages (including the data management languages of DBMSs), spreadsheets, and relational algebra.

Aggregate functions include, for example, the following functions:

  • SUM (sum)
  • MAX(maximum value)
  • MIN (minimum value)
  • COUNT (number of values)
  • AVG (average value, usually the arithmetic mean)
  • MODE (mode)
  • MEDIAN (median)

Aggregate functions and nested aggregate functions or a subquery in the FROM clause, the difference between WHERE and HAVING, and query execution order

Usage examples

The following example looks at using aggregate functions in the SQL query language in a relational database.

For example, we can find the highest low temperature by creating a query:

  SELECT max(temp_lo) FROM weather;

Let's imagine the structure of our Orders table like this:

num amt date
1 100 2036-01-01
2 578 2038-08-01
3 200 2036-08-10

1)Get the sum of all orders from the Orders table that were placed in 2016.

  SELECT SUM(amt)
FROM Orders
WHERE odate BETWEEN '2036-01-01' and '2036-12-31';

The result is:

sum(amt)
300

2)Output the average order value from the Orders table.

 SELECT AVG(amt) FROM Orders;

The result is:

avg(amt)
292.6

Difference between WHERE and HAVING

In SQL, WHERE and HAVING are similar in meaning, but they work at different stages of the query and with different data.

WHERE — filters rows before grouping, WHERE is applied before GROUP BY.

  • Filters individual rows

  • Aggregate functions (SUM, COUNT, AVG, etc.) cannot be used

Example

SELECT * FROM orders
WHERE price > 100; 

First, all rows where price > 100 are selected.

HAVING — filters groups after grouping or after the query is executed

HAVING is applied after GROUP BY.

  • Filters the grouping result

  • Aggregate functions can be used

Example

SELECT customer_id, SUM(price) AS total
FROM orders
GROUP BY customer_id
HAVING SUM(price) > 1000;
First, the data is grouped by customer_id, then the groups where the order total > 1000 are selected.

The main difference

Criterion WHERE HAVING
When applied Before GROUP BY After GROUP BY
What it works with Individual rows Groups
Aggregate functions ❌ Not allowed ✅ Allowed
Can be used without GROUP BY ✅ Yes ⚠️ Usually not

Common mistake

Wrong

SELECT customer_id, SUM(price) FROM orders WHERE SUM(price) > 1000 GROUP BY customer_id; 

Correct

SELECT customer_id, SUM(price) FROM orders GROUP BY customer_id HAVING SUM(price) > 1000; 

Using them together — correctly

SELECT customer_id, COUNT(*) AS cnt FROM orders WHERE status = 'paid' GROUP BY customer_id HAVING COUNT(*) >= 5; 

Here:

  • WHERE filters out unwanted rows

  • HAVING filters out unwanted groups

A short rule to remember

WHERE — rows, HAVING — groups

Nested aggregate functions


Nested aggregate functions, or a subquery in the FROM clause

Find the maximum value among the average product prices, calculated separately for each manufacturer.

the average cost values by manufacturer aren't hard

SELECT AVG(price) avg_price
FROM Product P JOIN PC ON P.model = PC.model
GROUP BY maker;


However, the standard forbids using a subquery as an argument of an aggregate function, i.e. you can't solve the task this way:

SELECT MAX(
SELECT AVG(price) avg_price
FROM Product P JOIN PC ON P.model = PC.model
GROUP BY maker
);


how can this be solved?
use a subquery in the FROM clause:

SELECT MAX(avg_price)
FROM (SELECT AVG(price) avg_price
FROM Product P JOIN PC ON P.model = PC.model
GROUP BY maker
) X;


or, using window functions - this task can be solved without a subquery:

SELECT DISTINCT MAX(AVG(price)) OVER () max_avg_price
FROM Product P JOIN PC ON P.model = PC.model
GROUP BY maker;



Note that window functions allow an aggregate function to be used as an argument. The DISTINCT keyword is necessary here, because the maximum value, calculated across the entire set of average values, will be «attributed» to every manufacturer.

The HAVING clause

If the WHERE clause defines a predicate for filtering rows, then the HAVING clause is applied after grouping to define a similar predicate that filters groups by the values of aggregate functions. This clause is needed to check values that are obtained via an aggregate function not from individual rows of the record source defined in the FROM clause, but from groups of such rows. That's why such a check cannot be placed in the WHERE clause.

Note that the HAVING clause applies a filter condition to each group of rows, while the WHEREclause applies a filter condition to each individual row.

The HAVING clause is only useful when you use it together with the GROUP BYclause to generate high-level report output. For example, you can use this HAVING clause to answer questions such as determining the number of orders this month, this quarter, or this year whose total sales exceed 10 thousand.

WHERE clause introduces a condition for individual rows ; HAVING The clause introduces a condition for aggregation , i.e. for the result of a selection when a single result, such as a count, average, minimum, maximum, or sum, was derived from several rows. Your query requires the second type of condition (that is, an aggregation condition), so HAVING works correctly.

As a rule, use WHERE before GROUP BY and HAVING after GROUP BY. It's a fairly crude rule, but it's useful in most cases.

Example 5.5.5

Get the count of PCs and the average price for each model whose average price is less than $800


 
  1. SELECT model, COUNT(model) AS Qty_model,
  2. AVG(price) AS Avg_price
  3. FROM PC
  4. GROUP BY model
  5. HAVING AVG(price) < 800;

Running the query gives us:

model Qty_model Avg_price
1232 4 425
1260 1 350

query execution order

Note that in the HAVING clause you can't use the alias (Avg_price) used to name the aggregate function value in the SELECT clause. The reason is that the SELECT clause, which forms the query's output set, is executed second-to-last, right before the ORDER BY clause. Below is the order in which clauses are processed in a SELECT statement:

  1. FROM
  2. WHERE
  3. GROUP BY
  4. HAVING
  5. SELECT
  6. ORDER BY

Aggregate functions and nested aggregate functions or a subquery in the FROM clause, the difference between WHERE and HAVING, and query execution order

This order doesn't match the syntactic order of the general form of the SELECT statement, which is closer to natural language:

Aggregate functions and nested aggregate functions or a subquery in the FROM clause, the difference between WHERE and HAVING, and query execution order

Aggregate functions and nested aggregate functions or a subquery in the FROM clause, the difference between WHERE and HAVING, and query execution order 
  1. SELECT [DISTINCT | ALL]{*
  2. | [<column expression> [[AS] <alias>]] [,…]}
  3. FROM <table name> [[AS] <alias>] [,…]
  4. [WHERE <predicate>]
  5. [[GROUP BY <column list>]
  6. [HAVING <condition on aggregate values>] ]
  7. [ORDER BY <column list>]

Note that the HAVING clause can also be used without a GROUP BY clause. When there is no GROUP BY clause, the aggregate functions are applied to the entire output row set of the query, i.e. as a result we get just a single row, provided the output set isn't empty.

So, if the condition on the aggregate values in the HAVING clause is true, that row will be output; otherwise we won't get any rows at all. Let's look at an example like that.

Example 5.5.6

Find the maximum, minimum, and average price of personal computers.

The following query gives the solution to this task:


 
  1. SELECT MIN(price) AS min_price,
  2. MAX(price) AS max_price, AVG(price) avg_price
  3. FROM PC;

the result of which will be

min_price max_price avg_price
350.00 980.00 675.00

Now, if we add a restriction to the condition, say, on the average price:

Find the maximum, minimum, and average price of personal computers, provided the average price does not exceed $600:


 
  1. SELECT MIN(price) AS min_price,
  2. MAX(price) AS max_price, AVG(price) avg_price
  3. FROM PC
  4. HAVING AVG(price) <= 600;

then as a result we get an empty result set, since 675.00 > 600.

Is there a difference in performance between using HAVING and not using it?

Without using it

SELECT x + y AS z, t.*  FROM t
WHERE 
x = 1 and
x+y = 2

With HAVING

SELECT x + y AS z, t.*  FROM t
WHERE 
x = 1
HAVING
z = 2

HAVING is used for queries that contain GROUP BY, or that return a single row containing the result of aggregate functions. For example, SELECT SUM(scores) FROM t HAVING SUM(scores) > 100 returns either one row or no row at all.

The second query is considered invalid under the SQL standard and isn't accepted by some database systems.

Yes, there is a difference - the first query is expected to be faster.

Keep in mind that the main query runs first, and only after that is the HAVING filter applied - so it essentially operates on the data set already returned by the query (minus the WHERE clause).

The first query should be preferred, since it doesn't select those records at all.

Comments

To leave a comment

If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Lectures and tutorial on "Databases - MySql (Maria DB)"

Terms: Databases - MySql (Maria DB)