MAX

The MAX function computes the maximum based on a group of values. For the examples that follow, please reference the orders table within the order entry (oe) schema at livesql.oracle.com. The contents of this table can also be found below. This table contains one row for each order placed by a customer.

order_idorder_dateorder_modecustomer_idorder_statusorder_totalsales_rep_idpromotion_id
245816-AUG-07 03.34.12.234359 PMdirect101078279.6153
239719-NOV-07 02.41.54.696211 PMdirect102142283.2154
245402-OCT-07 05.49.34.678340 PMdirect10316653.4154
235414-JUL-08 06.18.23.234567 PMdirect104046257155
235808-JAN-08 05.03.12.654278 PMdirect10527826155
238114-MAY-08 08.59.08.843679 PMdirect106323034.6156
244031-AUG-07 09.53.06.008765 PMdirect107370576.9156
235708-JAN-06 08.19.44.123456 PMdirect108559872.4158
239410-FEB-08 09.22.35.564789 PMdirect109521863158
243502-SEP-07 11.22.53.134567 PMdirect144662303159
245520-SEP-07 11.34.11.456789 AMdirect145714087.5160
237916-MAY-07 02.22.24.234567 AMdirect146817848.2161
239602-FEB-06 01.34.56.345678 AMdirect147834930161
240629-JUN-07 04.41.20.098765 AMdirect14882854.2161
243413-SEP-07 05.49.30.647893 AMdirect1498268651.8161
243602-SEP-07 06.18.04.378034 AMdirect11686394.8161
244627-JUL-07 07.03.08.302945 AMdirect1178103679.3161
244727-JUL-08 08.59.10.223344 AMdirect101833893.6161
243214-SEP-07 09.53.40.223345 AMdirect1021010523163
243313-SEP-07 10.19.00.654279 AMdirect1031078163
235526-JAN-06 09.22.51.962632 AMonline104894513.5
235626-JAN-08 09.22.41.934562 AMonline105529473.8
235908-JAN-06 09.34.13.112233 PMonline10695543.1
236014-NOV-07 12.22.31.223344 PMonline1074990.4
236113-NOV-07 01.34.21.986210 PMonline1088120131.3
236213-NOV-07 02.41.10.619477 PMonline109492829.4
236323-OCT-07 05.49.56.346122 PMonline144010082.3
236428-AUG-07 06.18.45.942399 PMonline14549500
236528-AUG-07 07.03.34.003399 PMonline146927455.3
236628-AUG-07 08.59.23.144778 PMonline147537319.4
236727-JUN-08 09.53.32.335522 PMonline14810144054.8
236826-JUN-08 10.19.43.190089 PMonline1491060065
236926-JUN-07 11.22.54.009932 PMonline116011097.4
237027-JUN-08 12.22.11.647398 AMonline1174126
237116-MAY-07 01.34.56.113356 AMonline118679405.6
237227-FEB-07 12.22.33.356789 AMonline119916447.2
237327-FEB-08 01.34.51.220065 AMonline1204416
237427-FEB-08 02.41.45.109654 AMonline12104797
237526-FEB-07 03.49.50.459233 AMonline1222103834.4
237607-JUN-07 06.18.08.883310 AMonline123611006.2
237707-JUN-07 07.03.01.001100 AMonline141538017.8
237824-MAY-07 08.59.10.010101 AMonline142525691.3
238016-MAY-07 09.53.02.909090 AMonline143327132.6
238214-MAY-08 10.19.03.828321 AMonline144871173
238312-MAY-08 11.22.30.545103 AMonline145836374.7
238412-MAY-08 12.22.34.525972 PMonline146329249.1
238508-DEC-07 11.34.11.331392 AMonline1474295892
238606-DEC-07 12.22.34.225609 PMonline1481021116.9
238711-MAR-07 03.34.56.536966 PMonline149552758.9
238804-JUN-07 04.41.12.554435 PMonline1504282694.3
238904-JUN-08 05.49.43.546954 PMonline151417620
239018-NOV-07 04.18.50.546851 PMonline15297616.8
239127-FEB-06 05.03.03.828330 PMdirect153248070.6156
239221-JUL-07 08.59.57.571057 PMdirect154926632161
239310-FEB-08 07.53.19.528202 PMdirect155423431.9161
239502-FEB-06 08.19.11.227550 PMdirect156368501163
239819-NOV-07 09.22.53.224175 PMdirect15797110.3163
239919-NOV-07 10.22.38.340990 PMdirect158025270.3161
240010-JUL-07 01.34.29.559387 AMdirect159269286.4161
240110-JUL-07 02.22.53.554822 AMdirect1603969.2163
240202-JUL-07 03.34.44.665170 AMdirect1618600154
240301-JUL-07 04.49.13.615512 PMdirect1620220154
240401-JUL-07 04.49.13.664085 PMdirect1636510158
240501-JUL-07 04.49.13.678123 PMdirect16451233159
240729-JUN-07 07.03.21.526005 AMdirect16592519155
240829-JUN-07 08.59.31.333617 AMdirect1661309158
240929-JUN-07 09.53.41.984501 AMdirect167248154
241024-MAY-08 10.19.51.985501 AMdirect168645175156
241124-MAY-07 11.22.10.548639 AMdirect169815760.5156
241229-MAR-06 10.22.09.509801 AMdirect170966816158
241329-MAR-08 01.34.04.525934 PMdirect101548552161
241429-MAR-07 02.22.40.536996 PMdirect102810794.6153
241529-MAR-06 01.34.50.545196 PMdirect1036310161
241629-MAR-07 04.41.20.945676 PMdirect1046384160
241720-MAR-07 05.49.10.974352 PMdirect10551926.6163
241820-MAR-04 04.18.21.862632 PMdirect10645546.6163
241920-MAR-07 07.03.32.764632 PMdirect107331574160
242013-MAR-07 08.59.43.666320 PMdirect108229750160
242112-MAR-07 09.53.54.562432 PMdirect109172836
242216-DEC-07 08.19.55.462332 PMdirect144211188.5153
242321-NOV-07 10.22.33.362632 AMdirect145310367.7160
242421-NOV-07 10.22.33.263332 AMdirect146413824153
242516-NOV-06 11.34.22.162552 PMdirect14751500.8163
242617-NOV-06 12.22.11.262552 AMdirect14867200
242710-NOV-07 01.34.22.362124 AMdirect14979055163
242810-NOV-07 02.41.34.463567 AMdirect116814685.8
242910-NOV-07 03.49.25.526321 AMdirect117950125154
243002-OCT-07 06.18.36.663332 AMdirect101829669.9159
243114-SEP-06 07.03.04.763452 AMdirect10215610.6163
243701-SEP-06 08.59.15.826132 AMdirect103413550163
243801-SEP-07 09.53.26.934626 AMdirect10405451154
243931-AUG-07 10.19.37.811132 AMdirect105122150.1159
244101-AUG-08 11.22.48.734526 AMdirect10652075.2160
244227-JUL-06 12.22.59.662632 PMdirect107952471.9154
244327-JUL-06 01.34.16.562632 PMdirect10803646154
244427-JUL-07 02.22.27.462632 PMdirect109177727.2155
244527-JUL-06 03.34.38.362632 PMdirect14485537.8158
244818-JUN-07 04.41.49.262632 PMdirect14551388158
244913-JUN-07 05.49.07.162632 PMdirect146686155
245011-APR-07 06.18.10.362632 PMdirect14731636159
245117-DEC-07 05.03.52.562632 PMdirect148710474.6154
245206-OCT-07 08.59.43.462632 PMdirect149512589159
245304-OCT-07 09.53.34.362632 PMdirect1160129153
245607-NOV-06 07.53.25.989889 PMdirect11703878.4163
245731-OCT-07 11.22.16.162632 PMdirect118521586.2159

COMPUTE THE MAXIMUM ORDER TOTAL ACROSS ALL ORDERS

SELECT
    MAX(oe.orders.order_total) AS maximum_order_total
FROM
    oe.orders;
maximum_order_total
295892

In the example code above, note that a GROUP BY clause is not present because the code returns a single row representing the maximum order total value across all orders (295892).

COMPUTE THE MAXIMUM ORDER TOTAL FOR EACH CUSTOMER

SELECT
    oe.orders.customer_id,
    MAX(oe.orders.order_total) AS maximum_order_total
FROM
    oe.orders
GROUP BY
    oe.orders.customer_id;
customer_idmaximum_order_total
10770576.9
108120131.3
15825270.3
161600
166309
10529473.8
10992829.4
14327132.6
15969286.4
162220
163510
16845175
10494513.5
11879405.6
14536374.7
1214797
14138017.8
15523431.9
10178279.6
10313550
11614685.8
120416
14225691.3
15117620
15668501
1577110.3
16915760.5
14629249.1
148144054.8
149268651.8
122103834.4
1527616.8
17066816
14471173
11916447.2
15348070.6
1641233
16748
10242283.2
10623034.6
147295892
117103679.3
12311006.2
150282694.3
15426632
160969.2
1652519

When an aggregate must be computed across and for each set of grouped rows, a GROUP BY clause is necessary. Here, the rows have been grouped by customer ID and for each of those sets of grouped rows, the maximum order total is computed.

COMPUTE THE MAXIMUM ORDER TOTAL FOR THE TOP CUSTOMER(S) IN TERMS OF ORDERS PLACED

SELECT
    oe.orders.customer_id,
    MAX(oe.orders.order_total) AS maximum_order_total
FROM
    oe.orders
GROUP BY
    oe.orders.customer_id
HAVING
    COUNT(*) = (
		SELECT
			MAX(order_counts.order_count)
		FROM (
			SELECT
				COUNT(*) AS order_count
			FROM
				oe.orders
			GROUP BY
				oe.orders.customer_id
		) order_counts
	);
customer_idmaximum_order_total
14536374.7
14629249.1
148144054.8
149268651.8
14471173
147295892

The solution here is largely the same as computing the maximum order total for each customer; however, the addition of the HAVING clause permits the maximum order total to be computed only across the set(s) of grouped rows corresponding to the top customer(s) in terms of number of orders placed. Problems such as this one are often easier to tackle when the problem is broken down into two or more subproblems or steps. This problem can be broken down into three steps.

The first step is to write a query that returns the maximum order total for each customer. The second step is to write a query that returns a single value representing the largest number of orders placed by any customer. Luckily, neither of these problems are terribly difficult when solved individually. After solving for the two queries, it’s a simple as placing the query from step two in the HAVING clause as a subquery, as seen above. The most difficult part is realizing that this seemingly complex problem is a couple of simple problems that, when solved individually, the complexity disappears. Break-down problems into several subproblems or steps when you can.

RETRIEVE ORDER DETAILS FOR THE MOST RECENT ORDER PLACED

SELECT
    oe.orders.order_id,
    oe.orders.order_date,
    oe.orders.customer_id,
    oe.orders.order_total
FROM
    oe.orders
WHERE
    oe.orders.order_date = (
		SELECT
			MAX(oe.orders.order_date)
		FROM
			oe.orders
	);
order_idorder_datecustomer_idorder_total
244101-AUG-08 11.22.48.734526 AM1062075.2

The solution above utilizes a subquery in the WHERE clause. The subquery returns a single value, the maximum order date value within the orders table. This value is then compared to each row’s order date value in the outer query and if the values match, the corresponding row is returned. If multiple orders were placed at the same time (including fractional seconds), then more than one row would be returned, one for each order placed at that time.

RETRIEVE ORDER DETAILS FOR EACH CUSTOMER’S LARGEST ORDER(S) PLACED IN TERMS OF AMOUNT SPENT

-- Using a JOIN to a derived table to solve.
SELECT
    oe.orders.order_id,
    oe.orders.order_date,
    oe.orders.customer_id,
    oe.orders.order_total
FROM
    oe.orders
    INNER JOIN (
    SELECT
    	oe.orders.customer_id,
    	MAX(oe.orders.order_total) AS order_total
    FROM
    	oe.orders
    GROUP BY
    	oe.orders.customer_id
    ) largest_order
        ON oe.orders.customer_id = largest_order.customer_id
        AND oe.orders.order_total = largest_order.order_total;

-- Using common table expression and DENSE_RANK() to solve.
WITH largest_order AS (
    SELECT
    	oe.orders.order_id,
    	oe.orders.order_date,
    	oe.orders.customer_id,
    	oe.orders.order_total,
    	DENSE_RANK() OVER (
    		PARTITION BY oe.orders.customer_id
    		ORDER BY oe.orders.order_total DESC
    	) AS order_rank
    FROM
    	oe.orders
)
SELECT
    largest_order.order_id,
    largest_order.order_date,
    largest_order.customer_id,
    largest_order.order_total
FROM
    largest_order
WHERE
    largest_order.order_rank = 1;
order_idorder_datecustomer_idorder_total
245816-AUG-07 03.34.12.234359 PM10178279.6
239719-NOV-07 02.41.54.696211 PM10242283.2
238114-MAY-08 08.59.08.843679 PM10623034.6
244031-AUG-07 09.53.06.008765 PM10770576.9
243413-SEP-07 05.49.30.647893 AM149268651.8
244627-JUL-07 07.03.08.302945 AM117103679.3
235526-JAN-06 09.22.51.962632 AM10494513.5
235626-JAN-08 09.22.41.934562 AM10529473.8
236113-NOV-07 01.34.21.986210 PM108120131.3
236213-NOV-07 02.41.10.619477 PM10992829.4
236727-JUN-08 09.53.32.335522 PM148144054.8
237116-MAY-07 01.34.56.113356 AM11879405.6
237227-FEB-07 12.22.33.356789 AM11916447.2
237327-FEB-08 01.34.51.220065 AM120416
237427-FEB-08 02.41.45.109654 AM1214797
237526-FEB-07 03.49.50.459233 AM122103834.4
237607-JUN-07 06.18.08.883310 AM12311006.2
237707-JUN-07 07.03.01.001100 AM14138017.8
237824-MAY-07 08.59.10.010101 AM14225691.3
238016-MAY-07 09.53.02.909090 AM14327132.6
238214-MAY-08 10.19.03.828321 AM14471173
238312-MAY-08 11.22.30.545103 AM14536374.7
238412-MAY-08 12.22.34.525972 PM14629249.1
238508-DEC-07 11.34.11.331392 AM147295892
238804-JUN-07 04.41.12.554435 PM150282694.3
238904-JUN-08 05.49.43.546954 PM15117620
239018-NOV-07 04.18.50.546851 PM1527616.8
239127-FEB-06 05.03.03.828330 PM15348070.6
239221-JUL-07 08.59.57.571057 PM15426632
239310-FEB-08 07.53.19.528202 PM15523431.9
239502-FEB-06 08.19.11.227550 PM15668501
239819-NOV-07 09.22.53.224175 PM1577110.3
239919-NOV-07 10.22.38.340990 PM15825270.3
240010-JUL-07 01.34.29.559387 AM15969286.4
240110-JUL-07 02.22.53.554822 AM160969.2
240202-JUL-07 03.34.44.665170 AM161600
240301-JUL-07 04.49.13.615512 PM162220
240401-JUL-07 04.49.13.664085 PM163510
240501-JUL-07 04.49.13.678123 PM1641233
240729-JUN-07 07.03.21.526005 AM1652519
240829-JUN-07 08.59.31.333617 AM166309
240929-JUN-07 09.53.41.984501 AM16748
241024-MAY-08 10.19.51.985501 AM16845175
241124-MAY-07 11.22.10.548639 AM16915760.5
241229-MAR-06 10.22.09.509801 AM17066816
242810-NOV-07 02.41.34.463567 AM11614685.8
243701-SEP-06 08.59.15.826132 AM10313550

Retrieving the order details for each customer’s orders is straightforward. The difficulty in this problem is having to filter the orders to only the customer’s largest order(s) in terms of amount spent (i.e., order total). Like the problem above, this one can be broken down into a couple of steps.

First, we need to write a query returning the customer ID and corresponding order total associated with each customer’s largest order(s). We’ll use this information to filter the orders table to the largest order(s) placed by each customer by joining it to an outer query containing the details of the associated and matched order. You’ll notice we have two conditions in our ON clause. First, the customer ID in the outer-query and subquery must match and, second, the order total must match. The latter of the two is less obvious, but critical to our solution because we only want the largest order placed by each customer. In the absence of the second condition filtering on order total, the same result-set as retrieving all orders from the orders table would be returned.

A common table expression (CTE), containing each the details of each customer’s orders and the rank associated with that order when computed across only the respective customer’s orders, is used in the second solution. The SELECT statement that follows the CTE filters the temporary result set to only those records associated with each customers largest order(s) in terms of amount spent (i.e., largest_order.rank = 1).

Knowledge Check
Need more practice or simply wish to test your understanding? Give a few of the practice problems I’ve provided in the SQL Practice Problems section a try.