SQL Aliases
An alias is a temporary, query-scoped name you give to a column, an expression, or a table so the result set — or the query itself — is easier to read and write. Aliases don’t change anything in the underlying database; they only affect how a column is labeled in the output, or how a table is referred to within that single query. You’ll use them constantly: to give a calculated column a friendly name, to shorten long table names in multi-table joins, and to make aggregate results self-documenting instead of showing raw expressions like COUNT(*) as a column header.
Overview: What Aliases Are and How They Work
SQL has two kinds of aliases. A column alias renames an output column — the actual column, or the result of an expression like price * quantity or COUNT(*) — for the purposes of display and, in some cases, later clauses in the same query. A table alias gives a table (or a subquery, or the result of a join) a short, temporary name that you then use everywhere else in the query instead of the full table name.
It’s important to understand that a column alias is a label attached at the very last moment the query engine builds the output row — it does not create a new column in the table, it doesn’t persist between queries, and other sessions or later statements never see it. A table alias, by contrast, actually changes how the engine resolves column references while it plans and executes the query: once you write FROM employees AS e, the original name employees is no longer available elsewhere in that query (in most databases) — you must use e.
The keyword AS is optional in SQLite, MySQL, and PostgreSQL — SELECT salary total_pay FROM employees works exactly like SELECT salary AS total_pay FROM employees. SQL Server also supports AS but additionally allows total_pay = salary. Despite AS being optional, writing it explicitly is strongly recommended: it makes the alias visually obvious and prevents a very common bug where a missing comma between two column names accidentally turns the second column into an unintended alias for the first.
Syntax
-- Column alias
SELECT column_name AS alias_name
FROM table_name;
-- Table alias
SELECT alias_name.column_name
FROM table_name AS alias_name;
-- Alias containing spaces or a reserved word must be quoted
SELECT column_name AS "Alias With Spaces"
FROM table_name;
| Part | Meaning |
|---|---|
AS |
Optional keyword introducing the alias; recommended for readability |
alias_name |
The new name; must be quoted (with double quotes in standard SQL/SQLite, backticks in MySQL, or square brackets in SQL Server) if it contains spaces or is a reserved keyword |
| Column alias scope | Visible in ORDER BY (and, as a SQLite/MySQL/PostgreSQL extension, in GROUP BY); not visible in WHERE or HAVING |
| Table alias scope | Replaces the original table name for the rest of that query — used to qualify columns and required for self-joins |
Examples
Example 1: Basic column aliases
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
first_name TEXT,
last_name TEXT,
salary REAL
);
INSERT INTO employees (id, first_name, last_name, salary) VALUES
(1, 'Ana', 'Silva', 72000),
(2, 'Marco', 'Lee', 85000),
(3, 'Priya', 'Nair', 67000);
SELECT first_name AS "First Name",
last_name AS "Last Name",
salary AS annual_salary,
salary / 12 AS monthly_salary
FROM employees;
Result:
| First Name | Last Name | annual_salary | monthly_salary |
|---|---|---|---|
| Ana | Silva | 72000.0 | 6000.0 |
| Marco | Lee | 85000.0 | ≈7083.33 |
| Priya | Nair | 67000.0 | ≈5583.33 |
Notice that first_name needed no quoting because it has no spaces, but "First Name" does. The expression salary / 12 has no name of its own, so without an alias its header would just be the literal expression text — monthly_salary makes it self-explanatory.
Example 2: Table aliases in a join
CREATE TABLE customers (
customer_id INTEGER PRIMARY KEY,
customer_name TEXT
);
CREATE TABLE orders (
order_id INTEGER PRIMARY KEY,
customer_id INTEGER,
amount REAL
);
INSERT INTO customers (customer_id, customer_name) VALUES
(1, 'Acme Corp'),
(2, 'Globex');
INSERT INTO orders (order_id, customer_id, amount) VALUES
(101, 1, 250.00),
(102, 1, 99.50),
(103, 2, 430.00);
SELECT c.customer_name AS customer,
o.order_id AS order_id,
o.amount AS order_amount
FROM customers AS c
JOIN orders AS o ON c.customer_id = o.customer_id
ORDER BY o.order_id;
Result:
| customer | order_id | order_amount |
|---|---|---|
| Acme Corp | 101 | 250.0 |
| Acme Corp | 102 | 99.5 |
| Globex | 103 | 430.0 |
Here c and o are table aliases. Without them you’d have to write customers.customer_name and orders.order_id everywhere — tedious, and mandatory anyway once a join needs to disambiguate identically-named columns like customer_id in both tables.
Example 3: Aliasing aggregate results
CREATE TABLE products (
product_id INTEGER PRIMARY KEY,
product_name TEXT,
price REAL,
category TEXT
);
INSERT INTO products (product_id, product_name, price, category) VALUES
(1, 'Widget', 9.99, 'Hardware'),
(2, 'Gadget', 19.99, 'Electronics'),
(3, 'Gizmo', 14.99, 'Hardware'),
(4, 'Cable', 4.99, 'Hardware');
SELECT category AS product_category,
COUNT(*) AS num_products,
ROUND(AVG(price), 2) AS avg_price
FROM products
GROUP BY category
ORDER BY avg_price DESC;
Result:
| product_category | num_products | avg_price |
|---|---|---|
| Electronics | 1 | 19.99 |
| Hardware | 3 | 9.99 |
Aggregate expressions like COUNT(*) and AVG(price) are unreadable as column headers without an alias. This example also shows that ORDER BY avg_price works — SQLite, MySQL, and PostgreSQL all let ORDER BY reference a SELECT alias, because sorting happens after column names are assigned.
How It Works Step by Step (Under the Hood)
The reason aliases behave the way they do comes down to the logical order in which a SQL engine processes a query — which is different from the order you type the clauses in:
FROM/JOIN— tables are identified and, if aliased, given their temporary names first. From this point on, only the alias exists in the engine’s view of the query.WHERE— rows are filtered using the original column expressions. Column aliases fromSELECTdo not exist yet, becauseSELECThasn’t run.GROUP BY— remaining rows are collected into groups.HAVING— groups are filtered, again generally before aliases are assigned in the strict standard, though SQLite is lenient here as an extension.SELECT— the output columns, and their aliases, are finally computed and named.ORDER BY— sorting happens last (beforeLIMIT), by which point the aliases from step 5 exist, so they can be referenced.LIMIT/OFFSET— the row count is trimmed.
This single fact — that aliases are born in step 5, but WHERE runs in step 2 and HAVING in step 4 — explains almost every alias-related error you’ll hit, and it’s why ORDER BY is the one place aliases are always safe to use.
Common Mistakes
Mistake 1: Referencing a column alias in WHERE
CREATE TABLE employees (id INTEGER, name TEXT, salary REAL);
INSERT INTO employees VALUES (1,'Ana',72000),(2,'Marco',85000),(3,'Priya',65000);
SELECT salary AS annual_salary
FROM employees
WHERE annual_salary > 70000;
This fails with an error like no such column: annual_salary. As shown in the step-by-step order above, WHERE is evaluated before SELECT assigns the alias, so annual_salary simply doesn’t exist yet at that point in execution. The fix is to repeat the original expression in WHERE (or filter in an outer query wrapped around a subquery/CTE that already has the alias):
SELECT salary AS annual_salary
FROM employees
WHERE salary > 70000;
This returns Ana (72000) and Marco (85000); Priya is excluded because 65000 is not greater than 70000.
Mistake 2: Using a reserved word as an unquoted alias
CREATE TABLE employees (id INTEGER, department TEXT, salary REAL);
INSERT INTO employees VALUES (1,'Engineering',72000),(2,'Engineering',85000),(3,'Sales',60000);
SELECT department, COUNT(*) AS order
FROM employees
GROUP BY department;
This raises a syntax error, because ORDER is a reserved SQL keyword (it’s what introduces ORDER BY) and cannot be used unquoted as an identifier. The safest fix is simply to avoid reserved words in alias names entirely:
SELECT department, COUNT(*) AS employee_count
FROM employees
GROUP BY department
ORDER BY department;
This returns Engineering with a count of 2 and Sales with a count of 1. If you truly need a reserved word as an alias, quoting it (AS "order" in standard SQL/SQLite, or AS \`order\` in MySQL) works, but a descriptive non-reserved name is almost always clearer anyway.
Best Practices
- Always write
ASexplicitly, even though it’s optional — it prevents accidental aliases caused by a missing comma and makes queries easier for teammates to scan. - Use descriptive, snake_case or readable aliases for calculated and aggregate columns (
avg_price, nota1) so the output is self-documenting for anyone reading a report or exported CSV. - Prefer short, meaningful table aliases (
eforemployees,ofororders) in joins, and use them consistently to qualify every column once you have more than one table — it removes ambiguity and lets the query engine resolve columns unambiguously. - Avoid SQL reserved words as alias names; if you must use one, quote it consistently with your database’s quoting style.
- Remember aliases don’t exist in
WHEREor (in strict-standard databases)HAVING— repeat the expression there instead of the alias. - Always give self-joins distinct table aliases — a self-join is impossible to write without them, since the same table name can’t appear twice unqualified.
- Don’t rely on alias support in
GROUP BY/HAVINGif you need your SQL to be portable — it’s a common but non-standard extension; check the target database if portability matters.
Practice Exercises
- Using an
employeestable with columnsid,name, andsalary, write a query that selectsnameand aliasessalary * 0.1asbonus. - Write a query that joins a
customerstable to itself (using two different table aliases) to find pairs of customers who share the samecountry. Hint: alias the table twice, e.g.AS c1andAS c2, and join on matchingcountrywithc1.customer_id < c2.customer_idto avoid duplicate pairs. - Given an
orderstable withcustomer_idandamount, write a query that groups bycustomer_id, aliasesCOUNT(*)asorder_countandSUM(amount)astotal_spent, and sorts the result bytotal_spentdescending using the alias.
Summary
- A column alias renames an output column or expression for display purposes only; it doesn’t modify the underlying table.
- A table alias replaces a table’s name for the rest of the query, and is required for self-joins.
ASis optional but should always be written for clarity.- Aliases with spaces or reserved words must be quoted.
- Because of SQL’s logical processing order, aliases are unavailable in
WHEREand standardHAVING, but are always available inORDER BY, since sorting happens afterSELECTassigns the names. - Many databases (including SQLite) extend the standard by also allowing aliases in
GROUP BY— useful, but not universally portable.
