SELECT … FROM is the very first command you learn in SQL. Every read query starts here. In plain English:
SELECT — what you want
FROM — where to get it from
So SELECT name FROM users reads as "give me the name column from the users table". Sounds like a normal request — and 90% of working with a database is built on this.
Why SELECT matters
A database is a set of tables. To do anything with them — show on screen, return from an API, calculate stats — you first need to read the data. That's what SELECT is for.
Other commands (INSERT, UPDATE, DELETE) write or change data. SELECT is pure reading — safe and reversible: run a query, look at the result, nothing in the database has changed.
Basic syntax
SELECT column1, column2, ...
FROM table_name;
Example:
SELECT name, email
FROM users;
Reads as: "take the name and email columns from the users table".
The result is a list of all users, but only with two columns: name and email. Other columns (id, phone, signup date, etc.) don't appear in the result.
Pick everything: SELECT *
If you want all columns, there's a shortcut — the asterisk:
SELECT * FROM users;
Handy during development when you're exploring what's in a table. In real code (especially APIs and analytics), it's better to list columns explicitly. The asterisk is fragile: if someone adds a new column to the table, the query suddenly returns extra data and your app might break.
Rule of thumb: exploring a table — SELECT *. Writing production code — list columns by hand.
Example: a social-network posts table
Let's say there's a posts table:
| id | author | title | likes | created_at |
|---|
| 1 | anna | My first post | 12 | 2024-03-01 |
| 2 | bob | Cute cats | 87 | 2024-03-02 |
| 3 | vera | Borscht recipe | 45 | 2024-03-03 |
Query:
SELECT title, likes
FROM posts;
Result:
| title | likes |
|---|
| My first post | 12 |
| Cute cats | 87 |
| Borscht recipe | 45 |
The id, author, and created_at columns are still in the table — we just didn't ask for them.
Aliases: SELECT … AS …
Sometimes the column name is ugly — too long, in another language, or just inconvenient in the result. That's what AS is for:
SELECT
title AS heading,
likes AS hearts,
created_at AS posted_on
FROM posts;
Result:
| heading | hearts | posted_on |
|---|
| My first post | 12 | 2024-03-01 |
| Cute cats | 87 | 2024-03-02 |
| Borscht recipe | 45 | 2024-03-03 |
Same data — only the column headers in the output changed. AS can be omitted: title heading works the same.
Computed fields
In SELECT you can put not just column names but expressions — the database computes them on the fly. Super useful:
SELECT
title,
likes,
likes * 2 AS doubled_likes,
LENGTH(title) AS title_length
FROM posts;
Result:
| title | likes | doubled_likes | title_length |
|---|
| My first post | 12 | 24 | 13 |
| Cute cats | 87 | 174 | 9 |
| Borscht recipe | 45 | 90 | 14 |
likes * 2 and LENGTH(title) aren't real columns in the table — they're computed values. Comes in handy whenever you need a metric or transformation: prices with tax, text length, concatenated names, and so on.
Another example: e-commerce products
Products table:
| id | name | price | weight_kg |
|---|
| 1 | iPhone 15 | 999 | 0.17 |
| 2 | Bosch kettle | 49 | 1.20 |
| 3 | Book "Clean Code" | 22 | 0.65 |
Let's display the name, price in dollars, and price in euros (rate 0.92):
SELECT
name,
price AS price_usd,
ROUND(price * 0.92, 2) AS price_eur
FROM products;
Result:
| name | price_usd | price_eur |
|---|
| iPhone 15 | 999 | 919.08 |
| Bosch kettle | 49 | 45.08 |
| Book "Clean Code" | 22 | 20.24 |
The database multiplied and rounded for us. The price_eur column is computed; there's no such column in the table.
DISTINCT — drop duplicates
Sometimes you want only unique values in the result. For example, which countries do users come from:
SELECT DISTINCT country
FROM users;
If the table has 10,000 users from 5 countries, you get exactly 5 rows back, no repeats.
DISTINCT also works across multiple columns — uniqueness is then per combination:
SELECT DISTINCT country, city
FROM users;
DISTINCT is a big topic on its own and deserves its own article, but for now it's enough to know: "it dedupes rows in the result".
Bigger example: before and after
employees table:
| id | first_name | last_name | department | salary |
|---|
| 1 | Anna | Ivanova | IT | 4000 |
| 2 | Boris | Petrov | Sales | 2700 |
| 3 | Vera | Sidorova | IT | 5000 |
| 4 | Grisha | Nikiforov | HR | 2300 |
Query: "full name, department, and monthly salary with a 20% bonus".
SELECT
first_name || ' ' || last_name AS full_name,
department,
salary,
ROUND(salary * 1.2, 0) AS salary_with_bonus
FROM employees;
(|| is the string concatenation operator in PostgreSQL. In MySQL, use CONCAT(first_name, ' ', last_name) instead.)
Result:
| full_name | department | salary | salary_with_bonus |
|---|
| Anna Ivanova | IT | 4000 | 4800 |
| Boris Petrov | Sales | 2700 | 3240 |
| Vera Sidorova | IT | 5000 | 6000 |
| Grisha Nikiforov | HR | 2300 | 2760 |
Nothing in the table changed. SELECT just calculated and displayed. Run it again — same result.
Common beginner mistakes
1. Typos in column names. SQL is strict about identifiers. SELECT nme FROM users fails with column "nme" does not exist. Double-check spelling.
2. SELECT * in production. Works, but is unpredictable. Tomorrow someone adds a column with binary blobs — and your query is suddenly 10× slower. List columns explicitly.
3. Mixing up SELECT and FROM order. Beginners sometimes write FROM users SELECT name. The order is fixed: SELECT first, then FROM.
4. Forgetting a comma between columns.
SELECT name email FROM users;
The database thinks email is an alias for name. You get one column with header "email". Silently, no error. Hours of debugging later you realise.
5. Quotes around column names. If the column name is a normal word with no spaces — no quotes needed: SELECT name. For names with spaces or special characters (a bad idea, but it happens), PostgreSQL uses double quotes: SELECT "first name". MySQL uses backticks: SELECT `first name`.
6. Expecting a fixed order. SELECT * FROM users without ORDER BY can return rows in any order — even different orders for two identical queries. If you need a sort, add ORDER BY.
Quick recap
SELECT … FROM is the foundation of any read query.
- You can put columns, expressions, functions, and constants in
SELECT.
* means "all columns". Convenient for exploration, sketchy in production.
AS renames a column in the output. Optional keyword.
DISTINCT dedupes rows in the result.
SELECT doesn't change the database — pure read.
Try it yourself
On the same employees table:
- Print just the names and salaries of all employees.
- Add a column with the annual salary (monthly × 12) named
annual_salary.
- Get the list of unique departments via
DISTINCT.
Write the queries on your own first, then check yourself. SQL reads left to right — SELECT what FROM where, like a normal phrase. Each exercise makes it more automatic.
SELECT … FROMis the very first command you learn in SQL. Every read query starts here. In plain English:SELECT— what you wantFROM— where to get it fromSo
SELECT name FROM usersreads as "give me thenamecolumn from theuserstable". Sounds like a normal request — and 90% of working with a database is built on this.Why SELECT matters
A database is a set of tables. To do anything with them — show on screen, return from an API, calculate stats — you first need to read the data. That's what
SELECTis for.Other commands (
INSERT,UPDATE,DELETE) write or change data.SELECTis pure reading — safe and reversible: run a query, look at the result, nothing in the database has changed.Basic syntax
SELECT column1, column2, ... FROM table_name;Example:
SELECT name, email FROM users;Reads as: "take the
nameandemailcolumns from theuserstable".The result is a list of all users, but only with two columns: name and email. Other columns (id, phone, signup date, etc.) don't appear in the result.
Pick everything: SELECT *
If you want all columns, there's a shortcut — the asterisk:
SELECT * FROM users;Handy during development when you're exploring what's in a table. In real code (especially APIs and analytics), it's better to list columns explicitly. The asterisk is fragile: if someone adds a new column to the table, the query suddenly returns extra data and your app might break.
Rule of thumb: exploring a table —
SELECT *. Writing production code — list columns by hand.Example: a social-network posts table
Let's say there's a posts table:
Query:
SELECT title, likes FROM posts;Result:
The
id,author, andcreated_atcolumns are still in the table — we just didn't ask for them.Aliases: SELECT … AS …
Sometimes the column name is ugly — too long, in another language, or just inconvenient in the result. That's what
ASis for:SELECT title AS heading, likes AS hearts, created_at AS posted_on FROM posts;Result:
Same data — only the column headers in the output changed.
AScan be omitted:title headingworks the same.Computed fields
In
SELECTyou can put not just column names but expressions — the database computes them on the fly. Super useful:SELECT title, likes, likes * 2 AS doubled_likes, LENGTH(title) AS title_length FROM posts;Result:
likes * 2andLENGTH(title)aren't real columns in the table — they're computed values. Comes in handy whenever you need a metric or transformation: prices with tax, text length, concatenated names, and so on.Another example: e-commerce products
Products table:
Let's display the name, price in dollars, and price in euros (rate 0.92):
SELECT name, price AS price_usd, ROUND(price * 0.92, 2) AS price_eur FROM products;Result:
The database multiplied and rounded for us. The
price_eurcolumn is computed; there's no such column in the table.DISTINCT — drop duplicates
Sometimes you want only unique values in the result. For example, which countries do users come from:
SELECT DISTINCT country FROM users;If the table has 10,000 users from 5 countries, you get exactly 5 rows back, no repeats.
DISTINCTalso works across multiple columns — uniqueness is then per combination:SELECT DISTINCT country, city FROM users;DISTINCT is a big topic on its own and deserves its own article, but for now it's enough to know: "it dedupes rows in the result".
Bigger example: before and after
employeestable:Query: "full name, department, and monthly salary with a 20% bonus".
SELECT first_name || ' ' || last_name AS full_name, department, salary, ROUND(salary * 1.2, 0) AS salary_with_bonus FROM employees;(
||is the string concatenation operator in PostgreSQL. In MySQL, useCONCAT(first_name, ' ', last_name)instead.)Result:
Nothing in the table changed.
SELECTjust calculated and displayed. Run it again — same result.Common beginner mistakes
1. Typos in column names. SQL is strict about identifiers.
SELECT nme FROM usersfails withcolumn "nme" does not exist. Double-check spelling.2. SELECT * in production. Works, but is unpredictable. Tomorrow someone adds a column with binary blobs — and your query is suddenly 10× slower. List columns explicitly.
3. Mixing up SELECT and FROM order. Beginners sometimes write
FROM users SELECT name. The order is fixed:SELECTfirst, thenFROM.4. Forgetting a comma between columns.
-- Bug: missing comma between name and email SELECT name email FROM users;The database thinks
emailis an alias forname. You get one column with header "email". Silently, no error. Hours of debugging later you realise.5. Quotes around column names. If the column name is a normal word with no spaces — no quotes needed:
SELECT name. For names with spaces or special characters (a bad idea, but it happens), PostgreSQL uses double quotes:SELECT "first name". MySQL uses backticks:SELECT `first name`.6. Expecting a fixed order.
SELECT * FROM userswithoutORDER BYcan return rows in any order — even different orders for two identical queries. If you need a sort, addORDER BY.Quick recap
SELECT … FROMis the foundation of any read query.SELECT.*means "all columns". Convenient for exploration, sketchy in production.ASrenames a column in the output. Optional keyword.DISTINCTdedupes rows in the result.SELECTdoesn't change the database — pure read.Try it yourself
On the same
employeestable:annual_salary.DISTINCT.Write the queries on your own first, then check yourself. SQL reads left to right —
SELECT what FROM where, like a normal phrase. Each exercise makes it more automatic.