Google BigQuery

BigQuery string functions: syntax and tested examples

A reference to the BigQuery string functions people look up most, each with an example and its real result: splitting, substrings, concatenation, length, search, replace, trim, padding and regular expressions. It also notes where each one behaves differently from what you would expect.

Google BigQueryAlyona SamovarVadym Kramarenko6 min read

BigQuery string functions: syntax and tested examples

This page covers the BigQuery string functions that get looked up most, with an example each. Every result shown was produced by running the query in BigQuery on 6 October 2026.

To do this Use Example Result
Split by a delimiter SPLIT SPLIT('a,b,c', ',') [a, b, c]
Take part of a string SUBSTR SUBSTR('BigQuery', 1, 3) Big
Join strings CONCAT, || 'a' || 'b' ab
Count characters LENGTH LENGTH('héllo') 5
Find text STRPOS STRPOS('BigQuery', 'Query') 4
Test for text CONTAINS_SUBSTR CONTAINS_SUBSTR('BigQuery', 'query') true
Test the start STARTS_WITH STARTS_WITH('BigQuery', 'Big') true
Replace text REPLACE REPLACE('2026-10-06', '-', '/') 2026/10/06
Remove spaces TRIM TRIM(' hi ') hi
Pad to a width LPAD LPAD('7', 3, '0') 007
Match a pattern REGEXP_EXTRACT REGEXP_EXTRACT('order-123', r'(\d+)') 123

The complete list is Google’s string functions reference.

Note: Originally published in January 2024. Fully updated in October 2026 with every example re-run.

SPLIT

SPLIT

SPLIT(value, delimiter) returns an array, not a string. That is the reason most first attempts fail: you cannot compare or concatenate the result directly.

SELECT
  SPLIT('a,b,,c', ','),
  -- [a, b, , c]
  SPLIT('a,b,c', ',')[OFFSET(1)],
  -- b
  SPLIT('a,b,c', ',')[SAFE_OFFSET(9)],
  -- NULL
  ARRAY_LENGTH(SPLIT('', ','));
  -- 1
  • OFFSET counts from 0, ORDINAL from 1.
  • Use SAFE_OFFSET on real data. OFFSET(9) on a three-element array stops the query; SAFE_OFFSET(9) returns NULL.
  • Two delimiters in a row produce an empty string, not a skipped element.
  • Splitting an empty string returns an array with one empty element, so its length is 1, not 0.

To turn the pieces into rows, use UNNEST:

SELECT part
FROM UNNEST(SPLIT('utm_source=google&utm_medium=cpc', '&'))
  AS part;
-- utm_source=google
-- utm_medium=cpc

More on working with the result in array functions.

SUBSTR, LEFT and RIGHT

SUBSTR, LEFT and RIGHT

SUBSTR(value, position, length) counts positions from 1. A negative position counts from the end.

SELECT
  SUBSTR('BigQuery', 4),      -- Query
  SUBSTR('BigQuery', 1, 3),   -- Big
  SUBSTR('BigQuery', -5),     -- Query
  LEFT('BigQuery', 3),        -- Big
  RIGHT('BigQuery', 5);       -- Query

SUBSTRING is an alias for SUBSTR.

The Turning Point

OWOX Data Marts

See your first report built in real time. 15 minutes.

  1. Connect your data warehouse
  2. Pick your metrics
  3. Get a live Google Sheets report

In the time it takes to write a ticket. Then imagine never writing that ticket again.

Book a Demo

We'll use your actual use case

CONCAT and the || operator

CONCAT and the || operator

SELECT
  CONCAT('order-', CAST(42 AS STRING)), -- order-42
  'a' || 'b',                           -- ab
  CONCAT('a', 'b', NULL);               -- NULL

One NULL argument makes the whole result NULL. This is the most common string bug in reports: a full name disappears because the middle name is missing. Wrap nullable parts in IFNULL(x, '') or COALESCE.

CONCAT needs strings, so cast numbers first. To join the values of an array, ARRAY_TO_STRING skips NULL elements by default:

SELECT
  ARRAY_TO_STRING(['a', NULL, 'c'], '-'),      -- a-c
  ARRAY_TO_STRING(['a', NULL, 'c'], '-', '?'); -- a-?-c

To join values across rows, use the aggregate STRING_AGG:

SELECT STRING_AGG(x, ', ' ORDER BY x)
FROM UNNEST(['b', 'a', 'c']) AS x;
-- a, b, c

LENGTH

LENGTH

SELECT
  LENGTH('héllo'),      -- 5
  BYTE_LENGTH('héllo'); -- 6

LENGTH counts characters. BYTE_LENGTH counts bytes, and an accented letter takes two. Use BYTE_LENGTH only when a size limit is in bytes.

Finding text: STRPOS, STARTS_WITH, CONTAINS_SUBSTR, LIKE

Finding text: STRPOS, STARTS_WITH, CONTAINS_SUBSTR, LIKE

SELECT
  STRPOS('BigQuery', 'Query'),           -- 4
  STRPOS('BigQuery', 'x'),               -- 0
  INSTR('a-b-c', '-', 1, 2),             -- 4
  STARTS_WITH('BigQuery', 'Big'),        -- true
  ENDS_WITH('BigQuery', 'ry'),           -- true
  CONTAINS_SUBSTR('BigQuery', 'query'),  -- true
  'BigQuery' LIKE '%query%',             -- false
  'BigQuery' LIKE '%Query%';             -- true
  • STRPOS returns 0 when the text is not found, not NULL and not -1.
  • INSTR finds the n-th occurrence: above, the second hyphen.
  • CONTAINS_SUBSTR ignores letter case. LIKE, STARTS_WITH and STRPOS do not. That one difference explains most “the filter returns nothing” problems. To make the others case-insensitive, compare LOWER(x).

REPLACE

REPLACE

SELECT REPLACE('2026-10-06', '-', '/');
-- 2026/10/06

REPLACE swaps every occurrence of an exact piece of text. It does not understand patterns; for those, use REGEXP_REPLACE below.

TRIM, LTRIM and RTRIM

TRIM, LTRIM and RTRIM

SELECT
  TRIM('  hi  '),       -- hi
  TRIM('--hi--', '-'),  -- hi
  LTRIM('xxhi', 'x'),   -- hi
  RTRIM('hi!!', '!');   -- hi

Without a second argument, the functions remove whitespace. With one, they remove any of the listed characters from the ends. TRIM before every join on a text key that came from a spreadsheet or a form: a trailing space is invisible and breaks the match.

UPPER, LOWER and INITCAP

UPPER, LOWER and INITCAP

SELECT
  UPPER('hi'),               -- HI
  LOWER('Hi'),               -- hi
  INITCAP('hello world');    -- Hello World

LPAD and RPAD

LPAD and RPAD

SELECT
  LPAD('7', 3, '0'),      -- 007
  RPAD('ab', 5, '.'),     -- ab...
  LPAD('12345', 3, '0');  -- 123

The last line is worth noticing: when the value is longer than the target width, LPAD cuts it. A five-digit ID padded to three characters loses two digits without an error.

Regular expressions

Regular expressions

BigQuery uses the RE2 syntax. Write patterns as raw strings, r'...', so backslashes do not need doubling.

SELECT
  REGEXP_CONTAINS('order-123', r'\d+'),
  -- true
  REGEXP_EXTRACT('order-123-us', r'(\d+)'),
  -- 123
  REGEXP_EXTRACT_ALL('a1b22c333', r'\d+'),
  -- [1, 22, 333]
  REGEXP_REPLACE('a  b   c', r'\s+', ' ');
  -- a b c

A practical one, the host from a URL:

SELECT REGEXP_EXTRACT(
  'https://www.owox.com/blog/articles/x?utm=1',
  r'^https?://([^/]+)');
-- www.owox.com

REGEXP_EXTRACT returns the first capture group, or the whole match if the pattern has none. It returns NULL when nothing matches. Regular expressions cost more to evaluate than plain functions, so where STARTS_WITH or REPLACE does the job, prefer it.

FORMAT, REVERSE and REPEAT

FORMAT, REVERSE and REPEAT

SELECT
  FORMAT('%s has %d orders', 'ana', 3),
  -- ana has 3 orders
  REVERSE('abc'),    -- cba
  REPEAT('ab', 3);   -- ababab

FORMAT builds a string from a pattern with placeholders and is easier to read than a long CONCAT.

Strings to other types

Strings to other types

A string that holds a number or a date has to be converted before you can calculate with it. Use SAFE_CAST for numbers and SAFE.PARSE_DATE for dates; both return NULL for values that do not convert. See CAST and SAFE_CAST and date functions.

The behaviours that cause wrong results

The behaviours that cause wrong results

Collected from the sections above, because each one fails silently:

  • CONCAT with a NULL returns NULL.
  • LIKE is case-sensitive; CONTAINS_SUBSTR is not.
  • SPLIT returns an array, and OFFSET past the end is an error.
  • STRPOS returns 0 for “not found”.
  • LPAD truncates a value longer than the width.
  • A trailing space makes two keys different.

Cleaning text once

Cleaning text once

String functions are mostly used for one job: turning text that arrived messy into keys and labels a report can group by. Campaign names get lower-cased, IDs padded, URLs cut down to a host. Each of those is a small decision, and the reports only agree with each other if every query makes it the same way.

That is the case for doing the cleaning in one query that the others read. In OWOX Data Marts that query is a Data Mart: your SQL with the TRIM, LOWER and REGEXP_EXTRACT you chose, saved with a description and an owner, and run in your warehouse. OWOX does not clean the text for you. It keeps the rules you wrote in one place, so a campaign is spelled the same in every report built on it.

Your New Normal

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
Get started free

FAQ

Frequently Asked Questions

How do I get the length of a string in BigQuery?

LENGTH(value) returns the number of characters. BYTE_LENGTH(value) returns the number of bytes, which is larger for accented and non-Latin characters: LENGTH('héllo') is 5 and BYTE_LENGTH('héllo') is 6.

What is the difference between ARRAY_AGG and STRING_AGG?

ARRAY_AGG collects values into an array and keeps their type. STRING_AGG joins string values into one string with a delimiter. Use ARRAY_AGG when you will process the values further, STRING_AGG when you need text to display.

How do I concatenate strings in BigQuery?

CONCAT(a, b) or the || operator. If any argument is NULL the result is NULL, so wrap nullable values in IFNULL(x, ''). Numbers must be cast to STRING first.

Why does LPAD cut my value in BigQuery?

LPAD(value, length, pad) returns exactly length characters. If the value is already longer, it is truncated: LPAD('12345', 3, '0') returns '123'. Check the maximum length of the column before choosing the width.

How do I check whether a string contains text in BigQuery?

CONTAINS_SUBSTR(value, 'text') returns true or false and ignores letter case. LIKE '%text%' and STRPOS are case-sensitive; STRPOS returns the position, or 0 when the text is not found.

How do I use STRING_AGG in BigQuery?

STRING_AGG(expression, delimiter ORDER BY expression) joins values across rows into one string, usually with GROUP BY. It returns a STRING and skips NULL values.

How do I split a string in BigQuery?

SPLIT(value, delimiter) returns an array. Take one element with SPLIT(x, ',')[SAFE_OFFSET(0)], which returns NULL instead of an error when the element does not exist, or turn the elements into rows with UNNEST(SPLIT(x, ',')).

How do I extract part of a string in BigQuery?

SUBSTR(value, position, length) for a fixed position, counting from 1; a negative position counts from the end. LEFT and RIGHT take characters from either end. For a pattern, use REGEXP_EXTRACT, which returns the first capture group.

Who wrote this

Alyona Samovar

Alyona Samovar · Senior Digital Analyst

Alyona Samovar is a Senior Digital Analyst who spent years at OWOX helping enterprise clients design analytics systems, build BigQuery reporting pipelines, and optimize marketing measurement. She specializes in SQL-based analytics, data modeling, and turning raw marketing data into actionable insights. Alyona writes about practical analytics workflows, BigQuery best practices, and data-driven decision-making.

Vadym Kramarenko

Vadym Kramarenko · Growth Marketing Manager

Vadym Kramarenko is a Growth Marketing Manager at OWOX, where he drives user acquisition and product-led growth strategies. He hosts the OWOX podcast, interviewing analytics professionals about data-driven marketing, attribution, and reporting best practices. Vadym specializes in turning complex analytics concepts into practical, actionable marketing frameworks.