SQL provides built-in functions for converting letters to their numeric ASCII values across all major database systems. Whether you are cleaning data, building search features, or implementing encoding logic directly in your queries, understanding how ASCII() and CHAR()/CHR() work is essential. For a quick visual reference, try our letters to numbers converter tool.
The ASCII() Function — Universal Across Databases
Every major SQL database supports the ASCII() function, which returns the numeric ASCII code of the first character in a string.
-- Works in MySQL, PostgreSQL, SQL Server, and Oracle
SELECT ASCII('A'); -- Returns 65
SELECT ASCII('Z'); -- Returns 90
SELECT ASCII('a'); -- Returns 97
SELECT ASCII('z'); -- Returns 122
-- For alphabet position (A=1), subtract 64
SELECT ASCII('A') - 64 AS position; -- Returns 1
SELECT ASCII('M') - 64 AS position; -- Returns 13
SELECT ASCII('Z') - 64 AS position; -- Returns 26The ASCII() function only processes the first character. For multi-character strings, ASCII('Hello') returns 72 (the code for H). See all ASCII values on our ASCII converter page.
Reverse Conversion: Number to Letter
The reverse function has different names depending on the database:
-- MySQL / SQL Server
SELECT CHAR(65); -- Returns 'A'
SELECT CHAR(90); -- Returns 'Z'
SELECT CHAR(13 + 64); -- Returns 'M' (position 13)
-- Oracle / PostgreSQL
SELECT CHR(65); -- Returns 'A'
SELECT CHR(90); -- Returns 'Z'
SELECT CHR(13 + 64); -- Returns 'M'Per-Database Syntax Comparison
| Operation | MySQL | PostgreSQL | Oracle | SQL Server |
|---|---|---|---|---|
| Letter to ASCII | ASCII('A') | ASCII('A') | ASCII('A') | ASCII('A') |
| ASCII to Letter | CHAR(65) | CHR(65) | CHR(65) | CHAR(65) |
| Uppercase | UPPER() | UPPER() | UPPER() | UPPER() |
| Substring | SUBSTRING() | SUBSTRING() | SUBSTR() | SUBSTRING() |
| String length | CHAR_LENGTH() | LENGTH() | LENGTH() | LEN() |
Converting Column Data: Single Character
-- Convert the first letter of each name to its position
-- Works in all databases (adjust SUBSTRING for Oracle)
SELECT
name,
ASCII(UPPER(SUBSTRING(name, 1, 1))) - 64 AS first_letter_position
FROM customers
ORDER BY first_letter_position;
-- Example output:
-- | name | first_letter_position |
-- |---------|-----------------------|
-- | Alice | 1 |
-- | Bob | 2 |
-- | Charlie | 3 |Processing Entire Strings in MySQL
MySQL does not have a native "map each character" function, so we use a recursive CTE or a helper approach:
-- MySQL 8.0+ recursive CTE approach
WITH RECURSIVE chars AS (
SELECT
'HELLO' AS original,
1 AS pos,
ASCII(UPPER(SUBSTRING('HELLO', 1, 1))) - 64 AS letter_pos
UNION ALL
SELECT
original,
pos + 1,
ASCII(UPPER(SUBSTRING(original, pos + 1, 1))) - 64
FROM chars
WHERE pos < CHAR_LENGTH(original)
)
SELECT
original,
GROUP_CONCAT(letter_pos ORDER BY pos SEPARATOR '-') AS encoded
FROM chars
WHERE letter_pos BETWEEN 1 AND 26;
-- Result: HELLO → 8-5-12-12-15Processing Entire Strings in PostgreSQL
PostgreSQL offers regexp_split_to_table() for elegant character-by-character processing:
-- PostgreSQL: Convert each character
SELECT string_agg(
(ASCII(UPPER(ch)) - 64)::text, '-' ORDER BY ord
)
FROM (
SELECT ch, row_number() OVER () AS ord
FROM regexp_split_to_table('HELLO', '') AS ch
) sub
WHERE ASCII(UPPER(ch)) BETWEEN 65 AND 90;
-- Result: 8-5-12-12-15
-- PostgreSQL function for reuse
CREATE OR REPLACE FUNCTION encode_a1z26(input_text TEXT)
RETURNS TEXT AS $$
SELECT string_agg(
(ASCII(UPPER(ch)) - 64)::text, '-' ORDER BY ord
)
FROM (
SELECT ch, row_number() OVER () AS ord
FROM regexp_split_to_table(input_text, '') AS ch
) sub
WHERE ASCII(UPPER(ch)) BETWEEN 65 AND 90;
$$ LANGUAGE SQL IMMUTABLE;
-- Usage
SELECT encode_a1z26('Hello World');
-- Result: 8-5-12-12-15-23-15-18-12-4Processing Entire Strings in Oracle
-- Oracle: CONNECT BY LEVEL for character iteration
SELECT LISTAGG(
ASCII(UPPER(SUBSTR('HELLO', LEVEL, 1))) - 64, '-'
) WITHIN GROUP (ORDER BY LEVEL) AS encoded
FROM dual
CONNECT BY LEVEL <= LENGTH('HELLO')
AND ASCII(UPPER(SUBSTR('HELLO', LEVEL, 1)))
BETWEEN 65 AND 90;
-- Result: 8-5-12-12-15Practical Use Case: Data Cleaning
A common real-world use is cleaning mixed alphanumeric data. For instance, extracting only the letter components and converting them:
-- MySQL: Extract letters from a product code and get positions
SELECT
product_code,
-- Remove digits, keep letters
REGEXP_REPLACE(product_code, '[^A-Za-z]', '') AS letters_only,
-- Position of first letter
ASCII(UPPER(SUBSTRING(
REGEXP_REPLACE(product_code, '[^A-Za-z]', ''), 1, 1
))) - 64 AS first_letter_pos
FROM products;
-- PostgreSQL: Same concept
SELECT
product_code,
regexp_replace(product_code, '[^A-Za-z]', '', 'g') AS letters_only
FROM products;Building a Lookup Table
For frequent conversions, a permanent lookup table is more efficient than calculating on every query. You can see all 26 mappings on our ASCII table page.
-- Create a reference table
CREATE TABLE alphabet_lookup (
letter CHAR(1) PRIMARY KEY,
position INT NOT NULL,
ascii_code INT NOT NULL
);
-- Populate it (PostgreSQL / MySQL)
INSERT INTO alphabet_lookup (letter, position, ascii_code)
SELECT
CHR(i) AS letter,
i - 64 AS position,
i AS ascii_code
FROM generate_series(65, 90) AS s(i); -- PostgreSQL
-- MySQL equivalent
INSERT INTO alphabet_lookup (letter, position, ascii_code)
WITH RECURSIVE nums AS (
SELECT 65 AS i
UNION ALL
SELECT i + 1 FROM nums WHERE i < 90
)
SELECT CHAR(i), i - 64, i FROM nums;
-- Now use JOINs for fast lookups
SELECT al.position
FROM alphabet_lookup al
WHERE al.letter = 'M'; -- Returns 13SQL Server Specific: T-SQL Function
-- SQL Server: Create a scalar function
CREATE FUNCTION dbo.EncodeA1Z26 (@text NVARCHAR(MAX))
RETURNS NVARCHAR(MAX)
AS BEGIN
DECLARE @result NVARCHAR(MAX) = '';
DECLARE @i INT = 1;
DECLARE @c CHAR(1);
DECLARE @pos INT;
WHILE @i <= LEN(@text) BEGIN
SET @c = UPPER(SUBSTRING(@text, @i, 1));
SET @pos = ASCII(@c) - 64;
IF @pos BETWEEN 1 AND 26 BEGIN
IF LEN(@result) > 0 SET @result = @result + '-';
SET @result = @result + CAST(@pos AS VARCHAR);
END
SET @i = @i + 1;
END
RETURN @result;
END;
-- Usage
SELECT dbo.EncodeA1Z26('Hello'); -- '8-5-12-12-15'See it instantly: Our ASCII Converter shows the ASCII value for any character. No SQL needed.
Frequently Asked Questions
How do I convert a letter to a number in SQL?
Use ASCII('A') to get 65, then subtract 64 for the alphabet position: ASCII('A') - 64 returns 1. The ASCII() function works identically in MySQL, PostgreSQL, Oracle, and SQL Server. It always returns the numeric code of the first character.
What is the difference between ASCII() in MySQL and Oracle?
The ASCII() function behaves identically in both databases. The only difference is the reverse function: MySQL uses CHAR() while Oracle uses CHR(). PostgreSQL supports both CHR() and has ASCII() as well.
Can I convert an entire column of letters to numbers in SQL?
Yes, use ASCII(UPPER(SUBSTRING(column, 1, 1))) - 64 for the first character of each row. For full strings, use a recursive CTE (MySQL 8+), regexp_split_to_table() (PostgreSQL), or CONNECT BY LEVEL (Oracle) to process each character individually.
How do I convert numbers back to letters in SQL?
Use CHAR(number + 64) in MySQL/SQL Server or CHR(number + 64) in Oracle/PostgreSQL. For example, CHAR(1 + 64) returns 'A' and CHAR(26 + 64)returns 'Z'. Always validate the input is between 1 and 26.