BigQuery functions: a map of the main families, with examples
BigQuery has several hundred SQL functions, and nearly all everyday work uses nine families: aggregate, window, date and time, string, conversion, conditional, JSON, array and math. This page shows one tested example per family on the same five-row table, names the trap in each, and links to the full reference for every family.

BigQuery has several hundred SQL functions. Nearly all everyday queries use nine families of them. This page is the map: what each family is for, one example you can run, the mistake people make with it, and the guide that covers it in full.
| Family | Answers | Start with | Full guide |
|---|---|---|---|
| Aggregate | how many, how much | COUNT, SUM, AVG |
aggregate functions |
| Window | running totals, ranks, previous row | SUM() OVER, ROW_NUMBER |
window functions |
| Date and time | by month, days between | DATE_TRUNC, DATE_DIFF |
date functions |
| String | clean, split, match text | CONCAT, SPLIT, REGEXP_EXTRACT |
string functions |
| Conversion | change a type | CAST, SAFE_CAST |
CAST and SAFE_CAST |
| Conditional | if this, then that | CASE, IF, COALESCE |
conditional expressions |
| JSON | read a field from JSON | JSON_VALUE, JSON_QUERY |
JSON functions |
| Array | lists inside a row | UNNEST, ARRAY_AGG |
array functions |
| Math | divide, round | SAFE_DIVIDE, ROUND |
mathematical functions |
Note: Fully rewritten in October 2026. Every result below was produced by running the query in BigQuery on 7 October 2026.
The table used in every example
The table used in every example
Paste this in front of any query below and it runs as is, without a table of your own:
WITH orders AS (
SELECT 1 AS order_id, 'anna' AS customer,
DATE '2026-09-01' AS order_date,
120.0 AS amount, 'paid' AS status
UNION ALL SELECT 2, 'anna', DATE '2026-09-15', 80.0, 'paid'
UNION ALL SELECT 3, 'ben', DATE '2026-09-20', 200.0, 'refunded'
UNION ALL SELECT 4, 'ben', DATE '2026-10-02', 50.0, 'paid'
UNION ALL SELECT 5, 'cara', DATE '2026-10-05', NULL, 'paid'
)
Five orders, three customers, and one order with no amount, because real tables have those.
Aggregate functions
They turn many rows into one.
SELECT
COUNT(*) AS orders,
COUNT(amount) AS with_amount,
SUM(amount) AS total,
AVG(amount) AS avg_amount
FROM orders;
| orders | with_amount | total | avg_amount |
|---|---|---|---|
| 5 | 4 | 450 | 112.5 |
The trap: COUNT(*) counts rows, COUNT(amount) counts rows where amount is not NULL, and AVG divides by the second number. The average here is 450 / 4, not 450 / 5. More in aggregate functions and statistical aggregates.
OWOX Data Marts
See your first report built in real time. 15 minutes.
- Connect your data warehouse
- Pick your metrics
- Get a live Google Sheets report
In the time it takes to write a ticket. Then imagine never writing that ticket again.
Book a DemoWe'll use your actual use case
Window functions
They calculate across related rows and keep every row.
SELECT
order_id, customer, amount,
SUM(amount) OVER (
PARTITION BY customer ORDER BY order_date
) AS running,
ROW_NUMBER() OVER (
PARTITION BY customer ORDER BY order_date
) AS rn
FROM orders;
| order_id | customer | amount | running | rn |
|---|---|---|---|---|
| 1 | anna | 120 | 120 | 1 |
| 2 | anna | 80 | 200 | 2 |
| 3 | ben | 200 | 200 | 1 |
| 4 | ben | 50 | 250 | 2 |
| 5 | cara | NULL | NULL | 1 |
rn = 1 is each customer’s first order, the usual way to pick one row per group. To filter on it without a subquery, use QUALIFY; see WHERE, HAVING and QUALIFY. The family splits into numbering and navigation functions.
Date and time functions
SELECT
FORMAT_DATE('%Y-%m',
DATE_TRUNC(order_date, MONTH)) AS month,
SUM(amount) AS total
FROM orders
GROUP BY month
ORDER BY month;
| month | total |
|---|---|
| 2026-09 | 400 |
| 2026-10 | 50 |
The trap: there are three time types, and the functions are named after them. A TIMESTAMP is truncated in UTC unless you name a time zone, so a late-evening order can land in the next day or month. Which type you have decides which guide you need: date, datetime or timestamp functions. The difference between the types is in DATE, DATETIME and TIMESTAMP.
String functions
| Expression | Result |
|---|---|
UPPER(CONCAT('ord-', CAST(7 AS STRING))) |
ORD-7 |
SPLIT('a,b,c', ',')[OFFSET(1)] |
b |
REGEXP_EXTRACT('utm_source=google&utm_medium=cpc', r'utm_source=([^&]+)') |
The trap: CONCAT with a NULL argument returns NULL, and comparisons are case-sensitive. The string functions guide has both.
Conversion functions
| Expression | Result |
|---|---|
CAST('2026-10-07' AS DATE) |
2026-10-07 |
SAFE_CAST('12x' AS INT64) |
NULL |
CAST fails the whole query on a value it cannot convert. SAFE_CAST returns NULL for that value and carries on. That is convenient, and it also hides bad data, so count the NULLs it produced. See CAST and SAFE_CAST and the conversion functions lookup.
Conditional expressions
SELECT
order_id,
IF(status = 'paid', 'keep', 'drop') AS flag,
COALESCE(amount, 0) AS amount0,
CASE
WHEN amount >= 100 THEN 'large'
WHEN amount IS NULL THEN 'unknown'
ELSE 'small'
END AS size
FROM orders;
| order_id | flag | amount0 | size |
|---|---|---|---|
| 1 | keep | 120 | large |
| 2 | keep | 80 | small |
| 3 | drop | 200 | large |
| 4 | keep | 50 | small |
| 5 | keep | 0 | unknown |
The trap: CASE stops at the first match, so order the branches from specific to general. And COALESCE(amount, 0) changes an average: order 5 now counts as a zero. More in conditional expressions and COALESCE.
JSON functions
| Expression | Result |
|---|---|
JSON_VALUE('{"user":{"plan":"pro"}}', '$.user.plan') |
pro |
JSON_VALUE returns a scalar as a string; JSON_QUERY returns an object or an array. Picking the wrong one returns NULL without an error. See JSON parsing functions.
Array functions
| Expression | Result |
|---|---|
ARRAY_LENGTH([1, 2, 3]) |
3 |
(SELECT SUM(x) FROM UNNEST([1, 2, 3]) AS x) |
6 |
UNNEST turns an array into rows. It is how you read repeated fields such as the event parameters in a GA4 export. Unnesting two arrays in one query multiplies rows; the array functions guide shows how to avoid it.
Math functions
| Expression | Result |
|---|---|
ROUND(10 / 3, 2) |
3.33 |
MOD(10, 3) |
1 |
SAFE_DIVIDE(10, 0) |
NULL |
A plain 10 / 0 fails the query. Any ratio built from real data, such as a conversion rate or cost per click, should use SAFE_DIVIDE. See mathematical functions.
Where functions stop helping
Functions answer “how do I write this”. They do not answer “which of our four versions of this query is right”. Two analysts can both use COALESCE correctly and still report different revenue, because one of them replaced missing amounts with zero and the other left them out.
That is a question of ownership, not syntax. In OWOX Data Marts a query is published once as a Data Mart, with a description on every column that says what was decided, and reports in Google Sheets or a read that one definition. OWOX Data Marts runs the SQL as you wrote it, in your own warehouse, and does not transform the data.
For how the clauses fit together, start with the BigQuery SQL guide. For complete queries to adapt, see BigQuery query examples.
Turn your data into decisions.
Governed data marts give you the clean foundation ML needs to actually work.
- No AI hallucinations
- Analyst-governed definitions
- Every number traces to SQL



