COUNT

The COUNT function computes the number of times an item (value) appears in 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 TOTAL NUMBER OF ORDERS

SELECT
    COUNT(*) AS order_count
FROM
    oe.orders;
order_count
105

In the example code above, note that a GROUP BY clause is not present because the code returns a single row representing the total number of orders across the entire orders table (105). Here, we’ve taken advantage of knowing each row within the orders table represents a single order. Therefore, to compute the total number of orders within the orders table, simply the number of rows needs to be computed. Keep this in mind for the examples that follow.

COMPUTE THE TOTAL NUMBER OF ORDER FOR EACH CUSTOMER

SELECT
    oe.orders.customer_id,
    COUNT(*) AS order_count
FROM
    oe.orders
GROUP BY
    oe.orders.customer_id;
customer_idorder_count
1074
1084
1581
1611
1661
1054
1094
1431
1591
1621
1631
1681
1044
1182
1455
1211
1411
1551
1014
1034
1164
1201
1421
1511
1561
1571
1691
1465
1485
1495
1221
1521
1701
1445
1191
1531
1641
1671
1024
1064
1475
1174
1231
1501
1541
1601
1651

Computing the total number of orders for each customer requires that we simply build onto our previous solution. A GROUP BY clause is necessary to retrieve the number of orders for each customer. The result set will contain one row of data per customer. Within each row will be the customer ID and the corresponding order count value.

RETRIEVE ONLY THOSE CUSTOMERS WITH FIVE OR MORE ORDERS

SELECT
    oe.orders.customer_id,
    COUNT(*) AS order_count
FROM
    oe.orders
GROUP BY
    oe.orders.customer_id
HAVING
    COUNT(*) >= 5;
customer_idorder_count
1455
1465
1485
1495
1445
1475

Like the approach taken in the previous example, simply build onto the previous solution. A HAVING clause is necessary to filter the grouped rows (i.e., the individual rows containing each customer’s order count). Filter by the number of rows corresponding to each customer (i.e., COUNT(*)).

RETRIEVE THE TOP CUSTOMER(S) IN TERMS OF ORDER COUNT

-- Using a subquery within a subquery.
SELECT
    oe.customers.customer_id,
    COUNT(oe.orders.order_id) AS order_count
FROM
    oe.customers
    INNER JOIN
    oe.orders
        ON oe.customers.customer_id = oe.orders.customer_id
GROUP BY
    oe.customers.customer_id
HAVING
    COUNT(oe.orders.order_id) = (
		SELECT
			MAX(order_counts.order_count)
		FROM (
			SELECT
				COUNT(oe.orders.order_id) AS order_count
			FROM
				oe.orders
			GROUP BY
				oe.orders.customer_id
		) order_counts
	);

-- Using a subquery in the HAVING clause to solve (nested-aggregate).
SELECT
    oe.customers.customer_id,
    COUNT(oe.orders.order_id) AS order_count
FROM
    oe.customers
    INNER JOIN
    oe.orders
    	ON oe.customers.customer_id = oe.orders.customer_id
GROUP BY
    oe.customers.customer_id
HAVING
    COUNT(oe.orders.order_id) = (
		SELECT
			MAX(COUNT(oe.orders.order_id))
		FROM
			oe.orders
		GROUP BY
			oe.orders.customer_id
	);

-- Using subquery in the HAVING clause with the modified comparison operator, ALL.
SELECT
    oe.orders.customer_id,
    COUNT(*) AS order_count
FROM
    oe.orders
GROUP BY
    oe.orders.customer_id
HAVING
    COUNT(*) >= ALL (
		SELECT
			COUNT(*)
		FROM
			oe.orders
		GROUP BY
			oe.orders.customer_id
	);
customer_idorder_count
1455
1465
1485
1495
1445
1475

The first and second solutions rely on a subquery in the HAVING clause. The subquery returns a single value representing the largest number of orders placed by any given customer. The number of orders placed by each customer is subsequently compared to this value with rows in the outer query containing the same value being kept in the result-set. Take caution in implementing the second solution as it contains a nested aggregate. Many RDBMS do not permit the direct nesting of aggregates and require the additional step seen in the first solution where the number of orders placed for each customer is computed, treated as a derived table or temporary result-set, and finally the maximum of those order counts is computed. The third solution above utilizes a subquery in the HAVING clause in conjunction with the modified comparison operator, ALL. The subquery returns a list of values with each value representing an individual customer’s order count. Each customer’s order count computed in the outer query is compared to the list generated by the subquery. If the customer’s order count value is greater than or equal the all the values in the list, then the row for that customer is included in the result set. It can be inferred that if the customer’s order count value, when compared to each individual value generated by the subquery, is greater than or equal all of the values, then it must be the largest value.

COUNT(*) vs. COUNT(attribute)
When using the COUNT function, it is important to understand the difference in the function’s behavior when the argument passed to the function is an attribute instead of the “*” character. When an attribute is passed to the function, the count returned represents the number of rows with non-null values for the specified attribute while COUNT(*) simply represents the number of rows returned. For clarity, see the example code and corresponding output below.

SELECT
	COUNT(*) AS order_count,
	COUNT(oe.orders.sales_rep_id) AS assisted_order_count
FROM
	oe.orders;
order_countassisted_order_count
10570

In the output above, COUNT(*) returns the number of rows (i.e., orders) within the orders table. When the sales representative ID attribute is passed into the COUNT function, the number of rows with a sales representative ID value populated is returned (70). This value tells us seventy orders were placed with the assistance of a sales representative.

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.