Recipe

Convert a number to a string

Short answer: CAST(n AS VARCHAR) (or TEXT, STRING) on every engine. For padding, decimals, or currency formatting: TO_CHAR(n, format) (Postgres / Oracle / Snowflake / Redshift), printf / format (DuckDB / SQLite, printf-style), FORMAT (MySQL, locale-aware; or SQL Server, .NET-style), FORMAT('%.2f', n) (BigQuery, printf-style). For dashboard-ready strings ('$1,234.56'), pre-format here; for downstream processing, raw casts are usually enough.

Canonical SQL #

sql
-- Portable.
SELECT CAST(42 AS VARCHAR);                   -- '42'
SELECT CAST(3.14 AS VARCHAR);                 -- '3.14'

-- Postgres / DuckDB / Snowflake shorthand.
SELECT 42::VARCHAR;
SELECT 3.14::TEXT;

-- Format-aware (Postgres / Oracle / Snowflake / Redshift).
SELECT TO_CHAR(1234.5, '999,999.99');         -- ' 1,234.50'
SELECT TO_CHAR(0.234, '99%');                  -- ' 23%' (* 100 implicit with %)
SELECT TO_CHAR(42, 'FM0009');                  -- '0042' (FM = no padding spaces)

-- DuckDB / SQLite (printf-style).
SELECT printf('%.2f', 3.14);                   -- '3.14'
SELECT printf('%05d', 42);                     -- '00042'

-- MySQL.
SELECT FORMAT(1234.5, 2);                      -- '1,234.50' (locale-aware)
SELECT CONCAT(42);                             -- implicit cast via concat

-- BigQuery.
SELECT FORMAT('%.2f', 3.14);                   -- '3.14' (printf-style)
SELECT FORMAT('%,d', 1234);                    -- '1,234'

-- SQL Server.
SELECT FORMAT(1234.5, 'N2');                   -- '1,234.50'
SELECT CONVERT(VARCHAR, 42);                   -- '42'

Example #

Loading SQL editor...

Common patterns #

sql
-- Format with two decimal places (defensive against FLOAT precision).
SELECT TO_CHAR(ROUND(price::NUMERIC, 2), 'FM999990.00') AS price_str FROM products;
SELECT FORMAT(ROUND(price, 2), 2)                        AS price_str FROM products;  -- MySQL

-- Build an ID with leading zeros.
SELECT 'ORD-' || LPAD(CAST(id AS VARCHAR), 6, '0') AS order_code FROM orders;

-- Display-ready currency string.
SELECT '$' || TO_CHAR(amount, 'FM999,999,999.00') AS display_amount FROM invoices;

-- Concatenate number into a sentence.
SELECT 'Order ' || order_id || ' totals $' || total AS message FROM orders;

-- Convert big numbers to k / M / B for compact display.
SELECT
  CASE
    WHEN total >= 1e9 THEN ROUND(total / 1e9, 1) || 'B'
    WHEN total >= 1e6 THEN ROUND(total / 1e6, 1) || 'M'
    WHEN total >= 1e3 THEN ROUND(total / 1e3, 1) || 'K'
    ELSE total::VARCHAR
  END AS compact
FROM revenue;

Dialect comparison #

EngineSyntaxGotcha
PostgreSQLTO_CHAR(n, 'FM999,990.00')9 pads with spaces unless prefixed with FM
MySQLFORMAT(n, 2)locale-aware — separators change with the connection locale
SQL ServerFORMAT(n, 'N2').NET format strings; slow on large row counts
BigQueryFORMAT('%.2f', n)printf-style, format string first
SnowflakeTO_CHAR(n, '999,990.00')Postgres/Oracle-style format model
OracleTO_CHAR(n, '999,990.00')a number too wide for the format prints # characters
SQLiteprintf('%.2f', n)format() is a synonym in 3.38+
DuckDBprintf('%.2f', n)no TO_CHARprintf / format only

Format string conventions #

  • Postgres / Oracle / Snowflake TO_CHAR: '9' (digit, prints space if zero), '0' (digit, prints zero), ',' (thousands sep), '.' (decimal point), 'FM' prefix to suppress padding spaces, '$' (currency sign at locale position). DuckDB has no TO_CHAR; its canonical number formatting is printf/format.
  • MySQL FORMAT(n, decimals): locale-aware; locale defaults to current connection.
  • SQL Server FORMAT: .NET-style format strings — 'N2', 'C', 'P', custom like '#,##0.00'. Slow on large datasets; STR and CONVERT are faster but less expressive.
  • BigQuery FORMAT: printf-style — '%d', '%f', '%.2f', '%,d'. Fast.
  • DuckDB / SQLite: printf function with same syntax as BigQuery.
Tip

**Format at the display layer, not at the warehouse layer.** Storing "$1,234.56" as a string in a database column means re-parsing every time you sum or compare. Store DECIMAL(10, 2) and let the BI tool, app code, or report renderer format. The exception: when SQL is the only place that touches the value before it reaches the human (e.g., emailing a CSV directly from a query). Otherwise keep numbers as numbers as long as possible. Detail: CAST, Round to decimal places.