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.