Convert a string to a number
Short answer: CAST(s AS INTEGER) (or DECIMAL, FLOAT) on every engine. Postgres / DuckDB / Snowflake also accept s::INTEGER. TRY_CAST (most engines) returns NULL instead of erroring on invalid input, which is useful when the source data isn't clean. Format-aware parsing (commas, currency symbols) usually requires TO_NUMBER or pre-stripping the string.
Canonical SQL #
-- Portable.
SELECT CAST('42' AS INTEGER);
SELECT CAST('3.14' AS DECIMAL(10, 4));
SELECT CAST('1.5e3' AS DOUBLE);
-- Postgres / DuckDB / Snowflake / Redshift shorthand.
SELECT '42'::INTEGER;
SELECT '3.14'::NUMERIC;
-- Safe — return NULL on parse failure.
SELECT TRY_CAST('not-a-number' AS INTEGER); -- DuckDB / SQL Server / Snowflake
SELECT SAFE_CAST('not-a-number' AS INT64); -- BigQuery
-- Postgres has no native TRY_CAST; use a regex pre-check or a CASE.
-- Format-aware parse.
SELECT TO_NUMBER('1,234.56', '999,999.99'); -- Postgres / Oracle / Snowflake
SELECT CAST(REPLACE('1,234.56', ',', '') AS DECIMAL); -- portable workaroundExample #
Common patterns #
-- Pre-clean before cast.
WITH listings(price_text) AS (
VALUES ('$1,299.00'), ('1 299 kr'), ('-42.50')
)
SELECT CAST(REGEXP_REPLACE(price_text, '[^0-9.\-]', '', 'g') AS DECIMAL(10, 2)) AS price
FROM listings;
-- Filter only rows where the cast would succeed (Postgres workaround for TRY_CAST).
WITH t(s) AS (VALUES ('42'), ('-7'), ('abc'))
SELECT CAST(s AS INTEGER) FROM t WHERE s ~ '^-?[0-9]+$';
-- Currency parsing.
SELECT CAST(REPLACE(REPLACE('$1,234.56', '$', ''), ',', '') AS DECIMAL(10, 2));
-- Percent parsing.
SELECT CAST(REPLACE('23.4%', '%', '') AS DECIMAL(5, 2)) / 100 AS rate;
-- Boolean-ish strings.
WITH raw_input(s) AS (VALUES ('true'), ('Yes'), ('0'), ('1'))
SELECT CASE LOWER(s)
WHEN 'true' THEN 1
WHEN 'yes' THEN 1
WHEN '1' THEN 1
ELSE 0
END AS as_bool
FROM raw_input;Dialect comparison #
| Engine | Syntax | Gotcha |
|---|---|---|
| PostgreSQL | CAST(s AS NUMERIC) | no TRY_CAST — errors on bad input; pre-validate with a regex |
| MySQL | CAST(s AS DECIMAL(10,2)) | '42abc' silently parses as 42 |
| SQL Server | TRY_CAST(s AS DECIMAL(10,2)) | plain CAST errors; TRY_CONVERT also available |
| BigQuery | SAFE_CAST(s AS NUMERIC) | the safe variant is SAFE_CAST, not TRY_CAST |
| Snowflake | TRY_CAST(s AS NUMBER) | TRY_TO_NUMBER adds a format model for '1,234.56' |
| Oracle | TO_NUMBER(s, '999,999.99') | DEFAULT NULL ON CONVERSION ERROR (12.2+) for NULL-on-fail |
| SQLite | CAST(s AS REAL) | never errors — non-numeric input becomes 0 |
| DuckDB | TRY_CAST(s AS INTEGER) | returns NULL on parse failure |
Dialect notes #
TRY_CAST: DuckDB, SQL Server, Snowflake, BigQuery (SAFE_CAST), MySQL (8.0+ viaCONVERTwith error handling). Not in Postgres.- Postgres: no
TRY_CAST. Either pre-validate with regex (s ~ '^-?[0-9]+$') or wrap the cast in a function withEXCEPTIONhandling. - MySQL is permissive:
CAST('42abc' AS INTEGER)returns42(parses as far as it can, no error). Often a bug source. TO_NUMBERformats: Postgres / Oracle / Snowflake share a format string syntax ('999,999.99'). Different fromprintf/formatstyle.- Implicit casts in some engines:
SELECT '42' + 1returns43on MySQL (auto-cast), errors on Postgres (mixed types). Don't rely on implicit casting.
Cast errors on string-to-number are usually data-quality bugs, not logic bugs. When CAST('1.234,56' AS DECIMAL) errors, the actual problem is the source data — European decimal format slipped through, or a CSV import didn't strip a header row, or a unit-of-measure suffix wasn't removed. The fix is upstream: clean the data at ingest, validate at the source, or change the column type. TRY_CAST quiets the symptom but masks the cause; combine it with a quality check that flags rows where the cast returned NULL. Detail: CAST.