The SQL SELECT Statement: A Beginner's Guide
On this page 13 sections ▾
- The basic shape
- Choosing specific columns
- Renaming columns with AS
- Removing duplicates with DISTINCT
- Sorting results with ORDER BY
- Limiting how many rows come back
- Calculations in the SELECT list
- Joining text together
- Three mistakes worth avoiding early
- Putting it together
- Practice this topic
- Frequently asked questions
- Where to go next
Almost every SQL query you'll ever write starts with the same word: SELECT.
It's how you ask a database to hand you rows of data. This guide walks through the SELECT statement piece by
piece, using a simple Employees table
as the running example. Every snippet below can be pasted straight into the
SQL playground, which already has this exact table loaded.
The basic shape
A SELECT statement has two required parts: which columns you want, and which table to get them from.
SELECT column1, column2
FROM table_name;
To get every column without typing each name, use the asterisk wildcard:
SELECT * FROM Employees;
This is convenient while exploring a table, but in real applications it's better practice to name the columns you actually need — it makes the query's intent clear and avoids pulling data you'll never use.
Choosing specific columns
SELECT first_name, last_name, salary
FROM Employees;
The column order in your SELECT list controls the column order in the result — it doesn't need to match the table's original definition.
Renaming columns with AS
The AS keyword
gives a column (or an expression) a friendlier name in the output, called an alias. It doesn't rename
anything in the actual table.
SELECT
first_name AS "First Name",
salary AS annual_salary
FROM Employees;
Aliases are especially useful once you start using calculations or aggregate functions, where the raw expression makes an ugly column header:
SELECT
first_name,
salary,
ROUND(salary * 12, 0) AS annual_salary
FROM Employees;
Removing duplicates with DISTINCT
If a column has repeated values and you only want to see each one once, add
DISTINCT right after SELECT:
SELECT DISTINCT dept_id
FROM Employees;
| dept_id |
|---|
| 10 |
| 20 |
| 30 |
| 50 |
● 4 rows · produced by running this query on the sample database
DISTINCT applies to the combination of every selected column, not each column independently. So
SELECT DISTINCT dept_id, manager_id
returns each unique pair of department and manager, not each unique department followed by each unique manager separately.
Sorting results with ORDER BY
Rows come back in no guaranteed order unless you ask for one. ORDER BY sorts ascending by default:
SELECT first_name, salary
FROM Employees
ORDER BY salary DESC;
Use ASC (default, so usually omitted) for
low-to-high, and DESC for high-to-low. You can
sort by multiple columns — the second column only breaks ties in the first:
SELECT dept_id, first_name, salary
FROM Employees
ORDER BY dept_id ASC, salary DESC;
Limiting how many rows come back
LIMIT caps the
number of rows returned — handy for previewing a large table or grabbing a "top N" list once combined with ORDER BY:
SELECT first_name, hire_date
FROM Employees
ORDER BY hire_date DESC
LIMIT 3;
| first_name | hire_date |
|---|---|
| Daniel | 2023-04-03 |
| Isabela | 2022-10-11 |
| Hiroshi | 2022-02-14 |
● 3 rows · produced by running this query on the sample database
That query returns the three most recent hires — LIMIT is evaluated after ORDER BY, so the sort happens first.
Calculations in the SELECT list
A SELECT list is not limited to columns that already exist. You can compute new ones from the row's values, and give the result a name with AS. Nothing is written back to the table — the calculation happens as the rows are returned:
SELECT name, unit_price, in_stock, unit_price * in_stock AS stock_value
FROM Products
ORDER BY stock_value DESC;
| name | unit_price | in_stock | stock_value |
|---|---|---|---|
| Beacon Analytics | 1450 | 999 | 1448550 |
| Atlas Cloud Suite | 899 | 999 | 898101 |
| Harbor Support Plan | 400 | 999 | 399600 |
| Aurora Laptop 14" | 1299 | 42 | 54558 |
| Vertex Monitor 27" | 449.99 | 68 | 30599.32 |
| Quill Keyboard | 119 | 210 | 24990 |
| Nimbus Docking Hub | 189.5 | 130 | 24635 |
● 7 rows · produced by running this query on the sample database
Functions work here too. ROUND is a common one, because a raw multiplication or division often produces more decimal places than anyone wants to read:
SELECT name, unit_price, ROUND(unit_price * 0.9, 2) AS sale_price
FROM Products
WHERE category = 'Hardware';
| name | unit_price | sale_price |
|---|---|---|
| Aurora Laptop 14" | 1299 | 1169.1 |
| Vertex Monitor 27" | 449.99 | 404.99 |
● 2 rows · produced by running this query on the sample database
Joining text together
The standard SQL operator for gluing strings together is ||, which works in SQLite, PostgreSQL
and Oracle. MySQL uses the CONCAT() function instead, and SQL Server uses +:
SELECT first_name || ' ' || last_name AS full_name, email
FROM Employees
ORDER BY full_name;
Note the ' ' in the middle. Without it you would get AnanyaRao — the operator joins
exactly what you give it and adds nothing of its own.
Three mistakes worth avoiding early
Reaching for SELECT * out of habit
It is genuinely useful while exploring an unfamiliar table. In anything you intend to keep, name your columns.
A saved query using * silently changes shape the day someone adds a column, and it moves far more
data than the query actually needs.
Expecting a reliable order without ORDER BY
Rows come back in whatever order the database found convenient. It often looks sorted, which is precisely the trap — that apparent order is not a promise, and it can change when the data grows or an index is added. If order matters, say so explicitly.
Assuming DISTINCT applies to one column
SELECT DISTINCT a, b removes duplicate combinations of a and b, not duplicates of a. It
is a modifier on the whole row the query returns, not on the column it happens to sit next to.
Putting it together
A more complete example, combining everything above:
SELECT
first_name,
last_name,
dept_id,
ROUND(salary, 0) AS salary
FROM Employees
ORDER BY salary DESC
LIMIT 5;
| first_name | last_name | dept_id | salary |
|---|---|---|---|
| Chen | Wei | 50 | 156000 |
| Ananya | Rao | 10 | 142000 |
| Tomas | Nowak | 20 | 131000 |
| Marcus | Bennett | 10 | 118000 |
| Priya | Menon | 20 | 105000 |
● 5 rows · produced by running this query on the sample database
Try it yourself
Open the SQL Playground — the Employees table is already loaded, so you can paste any query above and run it immediately.
Open the PlaygroundPractice this topic
Reading explains it; solving it is what makes it stick. These exercises use exactly what this page covered, and your answer is checked by running it against the same database:
- Just the names and prices Easy
- The three priciest items Easy
- Which countries do we sell to? Easy
- Companies beginning with A Medium
Frequently asked questions
What does SELECT * mean in SQL?
The asterisk is a wildcard meaning “every column in the table”. It is handy while exploring unfamiliar data, but in a query you intend to keep it is better to name the columns you actually want, so the result does not change shape when someone adds a column.
What is the difference between DISTINCT and GROUP BY?
DISTINCT removes duplicate rows from a result. GROUP BY collapses rows into groups so that aggregate functions can summarise each one. If you only want a de-duplicated list and no aggregates, DISTINCT states that intent more clearly.
How do I limit the number of rows returned?
Most databases use LIMIT, as in LIMIT 10. SQL Server uses SELECT TOP 10 instead, and Oracle uses FETCH FIRST 10 ROWS ONLY. A limit without an ORDER BY gives you an arbitrary ten rows rather than the first ten of anything meaningful.
Does the order of columns in SELECT matter?
Only for how the result is displayed — the columns come back in the order you list them. It has no effect on which rows are returned or on how the query is executed.
Can I use an alias defined in SELECT elsewhere in the same query?
In ORDER BY, yes, because ORDER BY runs after SELECT. In WHERE and HAVING it depends on the database, because in the standard those run before the aliases exist: PostgreSQL and SQL Server reject the alias in both, MySQL accepts it in HAVING but not in WHERE, and SQLite, which runs this site's playground, accepts it in both. Repeating the expression, or wrapping the query in a subquery, works everywhere.
Is SQL case-sensitive?
Keywords are not — SELECT and select are equivalent. Whether table and column names are case-sensitive depends on the database and, on some systems, the operating system. String values being compared are a separate question again, governed by collation.
Where to go next
Once SELECT feels comfortable, the next step is filtering rows with WHERE, then combining data from multiple tables with JOINs:
About the author
SQL Practice
SQL Practice publishes free SQL tutorials, an in-browser playground and 42 practice exercises, built so you can run what you read. Every runnable example in our tutorials is executed against the site’s sample database before it is published. Read how we keep tutorials accurate, or report a mistake.