Converting types with CAST
Turn text into numbers, numbers into text, and know what each database does when a value won't convert.
In this lesson 7 sections
Every value has a data type (lesson 9). CAST converts a value from one type to another:
CAST(value AS type)
SELECT CAST('42' AS INTEGER), -- text '42' becomes the number 42
CAST(42 AS TEXT), -- the number 42 becomes the text '42'
CAST(3.99 AS INTEGER); -- 3: the decimals are cut off, not rounded
Why you’d convert
| Problem | Symptom | Fix |
|---|---|---|
| Numbers stored as text | Sorts as 1, 10, 2, 20, and '10' < '9' is TRUE |
CAST(code AS INTEGER) before comparing or sorting |
| Whole-number division | 7 / 2 is 3 on several databases |
Make one side a decimal: CAST(7 AS REAL) / 2 is 3.5 |
| Joining mismatched keys | An ID is a number in one table and text in another, so nothing matches | CAST one side so both are the same type |
| Building a label | Gluing a number to text | `‘Deal ’ |
Here’s the first one in action:
SELECT '10' < '9', -- 1 (TRUE): compared as text
CAST('10' AS INTEGER) < CAST('9' AS INTEGER); -- 0 (FALSE): compared as numbers
Cutting off vs. rounding
Casting a decimal to a whole number cuts off the decimals. It doesn’t round:
SELECT stage,
AVG(amount) AS exact_avg,
CAST(AVG(amount) AS INTEGER) AS cut_off,
ROUND(AVG(amount)) AS rounded
FROM opportunities
WHERE stage = 'Closed Won';
| stage | exact_avg | cut_off | rounded |
|---|---|---|---|
| Closed Won | 40021.739… | 40021 | 40022 |
Neither is wrong. Pick on purpose: “whole dollars, ignore the cents” is CAST, “nearest dollar” is ROUND. Databases don’t agree here: SQLite and SQL Server cut off, while PostgreSQL and MySQL round. If the difference matters, use ROUND or a truncate function so the result doesn’t depend on the database.
When a value won’t convert
CAST('abc' AS INTEGER) has no sensible answer. Databases disagree about what to do:
| Database | CAST('abc' AS INTEGER) |
|---|---|
| SQL Server, PostgreSQL | Error. The whole query stops. |
| MySQL | 0, with a warning |
| SQLite (the sandbox) | 0, silently. CAST('12abc' AS INTEGER) is 12. |
SQLite’s silent answer is the dangerous one: bad data turns into real-looking zeros, and nothing warns you. When converting messy data on any database, check first which values won’t convert.
Dates are their own case
Dates in this sandbox are stored as text like '2026-10-01'. That’s fine: the next lesson’s date functions read that text directly. Don’t CAST it:
SELECT CAST('2026-10-01' AS DATE); -- SQLite returns 2026, not a date
SQLite has no real date type, so it treats DATE as a number and keeps only the leading 2026. On other databases, CAST('2026-10-01' AS DATE) works as you’d expect.
Common mistakes
| Mistake | Fix |
|---|---|
| Casting to a whole number when you meant to round | ROUND(x), or ROUND(x, 2) for cents |
Converting a column inside WHERE on a huge table |
Store the column as the right type when the data is loaded. Converting every row on every query is slow. |
| Trusting a cast of messy text | Find the values that won’t convert before you trust the result |
Dialect notes
| Database | Notes |
|---|---|
| SQL Server | Also CONVERT(INT, value). Casting a decimal to INT cuts off, like SQLite. TRY_CAST(value AS INT) returns NULL instead of an error. |
| PostgreSQL | Shorthand value::INTEGER. Casting 3.99 to an integer gives 4 (it rounds). |
| MySQL | Whole numbers cast to SIGNED (or UNSIGNED), decimals to DECIMAL(10,2). Casting 3.99 to SIGNED gives 4 (it rounds). TRUNCATE(3.99, 0) cuts off instead. |
Snowflake notes
| Syntax | What it does |
|---|---|
value::NUMBER |
Shorthand for CAST(value AS NUMBER) |
TRY_CAST(value AS NUMBER) |
Returns NULL instead of an error when a value won’t convert |
TRY_TO_NUMBER, TRY_TO_DATE |
Same idea, with format options for messy dates and numbers |
CAST('abc' AS NUMBER) stops with an error, like SQL Server. TRY_CAST is how you find bad values when loading data: WHERE TRY_CAST(raw_amount AS NUMBER) IS NULL AND raw_amount IS NOT NULL lists every row that won’t convert. Snowflake’s INTEGER is NUMBER(38,0), and casting 3.99 to it gives 4 (it rounds). Use TRUNC(x) to cut off instead.
More in the field guide: CAST / ::, Integer division and Numbers: precision and scale.
Try it
What’s the average deal amount in each stage, as whole dollars? Cut the cents off rather than rounding. Return the stage and the whole-dollar average, largest first.
Write it in the sandbox below, then press Check answer.
SQL sandbox
Run queries against the sample CRM tables (accounts, opportunities,users, contacts and opportunity_history), right in your browser. Nothing you run leaves your computer.