VMware ESXi and vSphere Cluster Management
SQL Functions: Aggregate and Scalar Functions
Learn how SQL functions calculate, summarize, transform, and format query results with aggregate and scalar function examples.
What is a SQL function?
A SQL function is a named operation that accepts zero or more inputs and returns a value. The inputs are called arguments. An argument can be a column, literal value, or larger expression.
Functions can calculate numbers, summarize rows, transform text, work with dates and times, handle missing values, and control how query output is displayed.
FUNCTION_NAME(argument1, argument2, ...)
For example, this scalar function converts each product name to uppercase:
SELECT UPPER(product_name) AS uppercase_name
FROM products;
Functions commonly appear in SELECT expressions. Depending on the database and the query, they may also appear in WHERE, HAVING, ORDER BY, and GROUP BY. Use them where their result is appropriate, and remember that function names and syntax can vary by database.
Aggregate functions and scalar functions
SQL functions are commonly divided into two categories: aggregate functions and scalar functions. An aggregate function is also called a set function.
- Aggregate functions process multiple rows and return one result for the complete set or one result for each group.
- Scalar functions process one value, row, or expression at a time and usually return one value for every selected row.
For example, an aggregate query can produce one average for an entire table:
SELECT AVG(price) AS average_price
FROM products;
A scalar expression produces a calculated value for each selected row:
SELECT product_name, ROUND(price * 1.10, 2) AS price_with_tax
FROM products;
The first query normally returns one row. The second normally returns one result row for every product selected.
Comparison of the two categories
- Aggregate input scope: many rows or one group.
- Aggregate result count: one result for the complete set, or one result per group.
- Scalar input scope: one value or expression at a time.
- Scalar result count: usually one result for each selected row.
Core aggregate functions
Aggregate functions summarize values across multiple rows. The most portable core aggregate functions are AVG(), COUNT(), MAX(), MIN(), and SUM().
AVG()
AVG() returns the average of numeric, non-NULL values.
SELECT AVG(price) AS average_price
FROM products;
The calculation is based on values that are present. Missing values represented by NULL are generally ignored.
COUNT()
COUNT(*) counts rows. COUNT(column) counts only rows where that column is not NULL.
SELECT
COUNT(*) AS total_products,
COUNT(discount_price) AS products_with_discount
FROM products;
If the table has 100 rows but 20 rows have a NULL discount price, the two results are 100 and 80.
SUM()
SUM() adds numeric, non-NULL values.
SELECT SUM(amount) AS total_sales
FROM sales;
MIN() and MAX()
MIN() returns the smallest value, while MAX() returns the greatest value in the input set. They can be used with numeric values, dates, and values that have a meaningful sort order.
SELECT
MIN(price) AS lowest_price,
MAX(price) AS highest_price
FROM products;
Like AVG() and SUM(), these functions generally ignore NULL values.
One summary row without GROUP BY
When an aggregate query has no GROUP BY, the database normally calculates one result for the complete filtered result set.
SELECT
AVG(price) AS average_price,
MIN(price) AS lowest_price,
MAX(price) AS highest_price
FROM products;
This query returns one summary row containing three calculated columns.
GROUP BY: one result per group
GROUP BY partitions rows into groups. An aggregate is then calculated separately for each group.
SELECT
category_id,
SUM(amount) AS category_sales
FROM sales
GROUP BY category_id;
This query returns one row for each distinct category_id. A selected column that is not inside an aggregate generally must be included in the GROUP BY list.
WHERE versus HAVING
WHERE filters individual source rows before grouping. HAVING filters groups after aggregate calculations.
SELECT
category_id,
SUM(amount) AS category_sales
FROM sales
WHERE sale_date >= DATE '2026-01-01'
GROUP BY category_id
HAVING SUM(amount) >= 10000;
WHEREremoves sales before they are grouped.GROUP BYcreates one group per category.SUM(amount)calculates each category's total.HAVINGkeeps only categories whose total is at least 10,000.
Filtering rows with WHERE before aggregation is often clearer and can reduce the amount of data that must be grouped.
FIRST() and LAST() warnings
Some database systems or older SQL environments expose FIRST() and LAST() as aggregate-style functions. They are not standard SQL, and their behavior differs between systems.
These names also do not inherently define which row is first or last. A table has no guaranteed natural order. To find an actual earliest or latest row, specify an ordering method and use the database's row-limiting syntax or a window function.
SELECT order_id, order_date
FROM orders
ORDER BY order_date DESC
FETCH FIRST 1 ROW ONLY;
The example uses standard-style row limiting supported by some databases. Other systems use a different clause. If several rows share the same date, add a tie-breaking column to ORDER BY, such as order_id.
Common scalar functions
A scalar function returns one value for each input row or expression. Scalar functions can be applied to columns, constants, and calculations.
Converting text case
UPPER() converts text to uppercase, and LOWER() converts text to lowercase. Some systems also provide the names UCASE() and LCASE().
SELECT
UPPER(customer_name) AS name_upper,
LOWER(customer_name) AS name_lower
FROM customers;
UPPER() and LOWER() are widely recognized alternatives. Check your database documentation before using UCASE() or LCASE().
Extracting part of a string
Text extraction functions return part of a text value. The starting position and requested length determine which characters are returned. Position numbering is dialect-specific, although many systems start at 1.
SELECT
product_code,
SUBSTRING(product_code FROM 1 FOR 3) AS product_prefix
FROM products;
Some databases use a different form, such as SUBSTRING(product_code, 1, 3), SUBSTR(product_code, 1, 3), or MID(product_code, 1, 3). The function name and parameter order must match your SQL dialect.
Finding text length
Text length functions report the length of a string. Common names include CHAR_LENGTH(), LENGTH(), and LEN().
SELECT
customer_name,
CHAR_LENGTH(customer_name) AS name_length
FROM customers;
LEN() and LENGTH() can have different treatment of trailing spaces or multibyte characters depending on the database. Use the documented function that matches whether you need character length or another type of length measurement.
Rounding numbers
ROUND(number, decimal_places) rounds a numeric value to the requested number of decimal places.
SELECT
order_id,
ROUND(quantity * unit_price, 2) AS line_total
FROM order_items;
Because the argument is an expression, the function calculates the line total first and then rounds it.
Current date and time
CURRENT_TIMESTAMP returns the current date and time in many SQL systems. NOW() is also common, but it is not equally portable and may have database-specific timing semantics.
SELECT CURRENT_TIMESTAMP AS query_time;
Formatting output
FORMAT() can format numbers or dates for display in databases that support it. Its arguments, formatting patterns, return type, and availability vary substantially.
SELECT FORMAT(price, 2) AS display_price
FROM products;
Treat this as dialect-dependent syntax. A database may instead provide separate date-formatting and number-formatting functions. When values still need to be sorted or calculated numerically, retain the original numeric or date value and format it in the presentation layer whenever practical.
Nesting and combining scalar functions
Functions can be nested. For example, this converts a name to uppercase and then measures the resulting text:
SELECT
customer_name,
CHAR_LENGTH(UPPER(customer_name)) AS uppercase_name_length
FROM customers;
Expressions can also combine functions with arithmetic, concatenation, comparisons, and other SQL expressions.
NULL values and function results
NULL represents a missing, unknown, or inapplicable value. It is not the same as zero, an empty string, or false.
Many scalar functions return NULL when their input is NULL. For example, UPPER(NULL) and ROUND(NULL, 2) typically return NULL.
COUNT(*)counts every row, including rows containingNULLvalues.COUNT(column)counts only non-NULLvalues in that column.SUM(column),AVG(column),MIN(column), andMAX(column)generally ignoreNULLinputs.- If every relevant input is
NULL, an aggregate such asSUM()orAVG()may returnNULL.
COALESCE() returns the first non-NULL expression in its argument list. It is useful when a fallback value is part of the intended calculation.
SELECT SUM(COALESCE(discount_amount, 0)) AS total_discount
FROM orders;
Here, each missing discount is explicitly treated as zero before summing. Use a fallback only when it accurately represents the business meaning of the missing data.
Aliases for readable results
An alias is a temporary name assigned to a result column or expression. Use AS to make calculated output easier to understand.
SELECT
ROUND(quantity * unit_price, 2) AS line_total,
UPPER(product_name) AS display_name
FROM order_items;
Aliases do not rename the underlying table column. They label the value in the query result.
SQL dialect differences and portability
A SQL dialect is a database system's variation of SQL. Function names, parameter order, return types, date rules, formatting patterns, and NULL behavior can differ.
UPPER()is a common alternative toUCASE().LOWER()is a common alternative toLCASE().SUBSTRING()orSUBSTR()can replaceMID(), but syntax varies.CHAR_LENGTH()orLENGTH()can replaceLEN()CURRENT_TIMESTAMPis a broadly recognized alternative toNOW().FIRST(),LAST(), andFORMAT()are especially dependent on the database system and context.
Before relying on a nonstandard function, consult the documentation for the database that will execute the query. Do not assume that a query written for one system will run unchanged on another.
Using functions safely and effectively
Consider indexes in WHERE conditions
An index is a data structure that can help a database find rows efficiently. Applying a function directly to an indexed column in a WHERE condition can prevent the database from using that index efficiently.
-- May make ordinary index use more difficult
SELECT *
FROM orders
WHERE UPPER(status) = 'PAID';
Where possible, compare the stored column directly or use a design supported by the database, such as a function-based index or a normalized value:
SELECT *
FROM orders
WHERE status = 'PAID';
The best solution depends on collation, case-sensitivity rules, indexes, and the database optimizer. Check the execution plan when performance matters.
Filter before aggregation
When a row filter is independent of an aggregate, put it in WHERE so irrelevant rows are removed before grouping.
SELECT category_id, SUM(amount) AS category_sales
FROM sales
WHERE sale_date >= DATE '2026-01-01'
GROUP BY category_id;
Keep display formatting separate from calculations
Formatting a number can turn it into text. Text that looks numeric may sort lexicographically rather than numerically. Keep the original numeric value for sorting, filtering, and calculations, and format it at the final display stage when possible.
Function syntax patterns
These patterns use generic names. Replace them with functions supported by your database.
SELECT FUNCTION_NAME(column_or_expression) AS result_name
FROM table_name;
SELECT grouping_column,
AGGREGATE_FUNCTION(value_column) AS result_name
FROM table_name
GROUP BY grouping_column;
SELECT grouping_column,
AGGREGATE_FUNCTION(value_column) AS result_name
FROM table_name
GROUP BY grouping_column
HAVING AGGREGATE_FUNCTION(value_column) condition;
No server configuration is required for these query-level functions. The supported names and syntax depend on the selected database platform.
Troubleshooting common problems
COUNT(column) returns fewer values than expected
The column probably contains NULL values. Use COUNT(*) to count rows, or use COUNT(column) when you specifically want to count populated values.
A query mixing a normal column and SUM() fails
A selected non-aggregated column may be missing from GROUP BY. Add it to the grouping list, aggregate it appropriately, or remove it from SELECT.
A function is not recognized
The function may belong to another SQL dialect. Try the supported equivalent, such as UPPER() instead of UCASE(), SUBSTRING() instead of MID(), CHAR_LENGTH() instead of LEN(), or a vendor-specific formatting function instead of FORMAT().
FIRST() or LAST() returns an unexpected row
There is no inherent table order, and these functions are not portable ordering mechanisms. Use an explicit ORDER BY with row limiting or use a window function.
SUM(), AVG(), or a text function returns NULL
The input may be NULL, or every relevant input may be NULL. Inspect the source data and use COALESCE() when a fallback is logically correct.
Formatted numbers sort incorrectly
The formatted result may be text. Sort by the original numeric expression and apply formatting only to the final displayed value.
A WHERE condition using a function is slow
The function may be preventing efficient index use. Where possible, rewrite the condition as a direct comparison or range on the original column, and reserve transformations for output.
Key points
- A SQL function is a named operation that returns a value from one or more arguments.
- Aggregate functions summarize many rows and return one result per set or group.
- Scalar functions return one value for each input row or expression.
GROUP BYcreates separate aggregate results, whileHAVINGfilters those groups.COUNT(*)counts rows;COUNT(column)counts non-NULLvalues.NULLis missing or unknown data, not zero or an empty string.UPPER(),LOWER(),SUBSTRING(),CHAR_LENGTH(),ROUND(), andCURRENT_TIMESTAMPare useful portable-oriented choices, but exact support still depends on the database.- Use aliases for readable output, verify dialect-specific syntax, and avoid unnecessary functions on indexed columns in filters.