SQL SELECT Statement – Syntax, Examples, and Query Order | DbSchema
Table of Contents
- What the SQL SELECT statement does
- SELECT syntax overview
- Logical query processing order
- SELECT specific columns vs SELECT *
- WHERE and DISTINCT
- Aliases, expressions, and CASE
- ORDER BY, LIMIT, TOP, and pagination
- GROUP BY, HAVING, and aggregate functions
- SELECT with JOINs and subqueries
- Common mistakes and best practices
- Build SELECT queries visually in DbSchema
- FAQ
- Conclusion
The SQL SELECT statement retrieves data from one or more tables. Almost every read query in SQL starts with SELECT, which is why it is the best place to learn how filtering, sorting, grouping, joins, and subqueries fit together.
Many beginner pages explain SELECT only as “choose columns from a table.” That is true, but incomplete. A real SELECT statement also shapes the result, controls the row set, and often powers applications, dashboards, exports, and APIs. This guide covers those practical pieces and shows how DbSchema helps you build and inspect queries visually.
What the SQL SELECT statement does
Use SELECT to:
- choose columns
- filter rows with
WHERE - remove duplicates with
DISTINCT - sort rows with
ORDER BY - group rows with
GROUP BY - filter grouped results with
HAVING - combine tables with
JOIN - nest queries with subqueries
Because all of those patterns live under one command, SELECT is the foundation for the rest of the SQL tutorial set.
SELECT syntax overview
SELECT [DISTINCT] column_list
FROM table_name
JOIN other_table ON condition
WHERE row_filter
GROUP BY grouping_columns
HAVING group_filter
ORDER BY sort_columns
LIMIT row_count OFFSET skip_count;
Not every clause is required. The simplest valid query is:
SELECT first_name, last_name
FROM customers;
Logical query processing order
One of the biggest sources of confusion is that SQL is written in one order but evaluated logically in another:
FROMJOINWHEREGROUP BYHAVINGSELECTDISTINCTORDER BYLIMIT/TOP/FETCH
That explains why:
- aliases created in
SELECTusually are not available inWHERE HAVINGfilters grouped rows, not raw rowsORDER BYsorts the final result set
SELECT specific columns vs SELECT *
Select only the columns you need
SELECT first_name, last_name, email
FROM customers;
This is usually better for readability, performance, and API stability.
Select all columns
SELECT *
FROM products;
SELECT * is fine for quick exploration, but it is risky in production because:
- new columns can appear unexpectedly
- sensitive columns may be returned accidentally
- more data is transferred than needed
WHERE and DISTINCT
Filter rows with WHERE
SELECT first_name, last_name, salary
FROM employees
WHERE department = 'Engineering'
AND salary > 70000;
Common WHERE operators include =, <>, >, <, BETWEEN, IN, LIKE, and IS NULL.
If you want a deeper walkthrough, continue with SQL WHERE Clause and SQL LIKE Operator.
Remove duplicates with DISTINCT
SELECT DISTINCT department
FROM employees;
DISTINCT works on the full combination of selected columns:
SELECT DISTINCT city, country
FROM customers;
For more examples, see SQL SELECT DISTINCT.
Aliases, expressions, and CASE
Column aliases
SELECT
first_name || ' ' || last_name AS full_name,
salary * 12 AS annual_salary
FROM employees;
Aliases make result sets easier to read, especially in reports and derived columns.
Expressions
SELECT
product_name,
price,
price * 1.19 AS price_with_tax
FROM products;
CASE expressions
SELECT
product_name,
price,
CASE
WHEN price < 20 THEN 'Budget'
WHEN price < 100 THEN 'Mid-range'
ELSE 'Premium'
END AS price_category
FROM products;
CASE lets you classify rows directly inside the query, which is common in dashboards and exports.
ORDER BY, LIMIT, TOP, and pagination
Sort the result set
SELECT first_name, last_name, hire_date
FROM employees
ORDER BY last_name ASC, hire_date DESC;
Limit returned rows
-- PostgreSQL, MySQL, SQLite
SELECT *
FROM products
ORDER BY price DESC
LIMIT 10;
-- SQL Server
SELECT TOP 10 *
FROM products
ORDER BY price DESC;
Paginate
SELECT *
FROM products
ORDER BY product_id
LIMIT 10 OFFSET 20;
If you paginate without a stable ORDER BY, page boundaries can shift unexpectedly. For a deeper sort guide, read SQL ORDER BY and SQL TOP, LIMIT, FETCH FIRST, and ROWNUM.
GROUP BY, HAVING, and aggregate functions
SELECT
department,
COUNT(*) AS employee_count,
AVG(salary) AS average_salary,
MAX(salary) AS max_salary
FROM employees
GROUP BY department;
GROUP BY creates one row per group. HAVING filters those grouped rows:
SELECT department, COUNT(*) AS employee_count
FROM employees
GROUP BY department
HAVING COUNT(*) > 5;
This is one of the most important transitions in SQL learning because it changes the result shape from detail rows to summary rows.
For deeper coverage, continue with SQL GROUP BY Explained, SQL HAVING Clause, and SQL COUNT, AVG, and SUM Functions.
SELECT with JOINs and subqueries
JOIN example
SELECT
o.order_id,
c.first_name,
c.last_name,
o.order_date,
o.total_amount
FROM orders o
INNER JOIN customers c
ON o.customer_id = c.customer_id
WHERE o.order_date >= '2024-01-01'
ORDER BY o.order_date DESC;
Subquery example
SELECT first_name, last_name, salary
FROM employees
WHERE salary > (
SELECT AVG(salary)
FROM employees
);
Derived-table example
SELECT department, avg_salary
FROM (
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
) AS department_stats
WHERE avg_salary > 80000;
Use joins when you need related columns from multiple tables. Use subqueries when a query depends on the result of another query. For more practice, read SQL Joins Explained.
Common mistakes and best practices
1. Using SELECT * everywhere
Great for exploration, poor for stable application queries.
2. Filtering aggregates in WHERE
This is wrong:
SELECT department, COUNT(*)
FROM employees
WHERE COUNT(*) > 5
GROUP BY department;
Use HAVING instead.
3. Forgetting ORDER BY during pagination
Without an explicit sort, the database is free to return rows in any order.
4. Confusing row filtering with group filtering
Use WHERE before grouping, HAVING after grouping.
5. Ignoring the schema
Many SELECT problems are really schema-understanding problems. DbSchema helps by keeping the relationships, keys, and table structures visible while you write the query.
Build SELECT queries visually in DbSchema
DbSchema includes a visual workflow for writing and understanding SELECT statements:
- Connect with the PostgreSQL JDBC driver, MySQL JDBC driver, or SQL Server JDBC driver.
- Inspect table relationships in the diagram before you write the query.
- Use the Query Builder to add tables, joins, filters, grouping, and sorting.
- Run the query and inspect the result in the Relational Data Explorer.
- Export the schema documentation if you need to explain the query context to teammates.
This is especially helpful for beginners because it connects the SQL text to the actual schema design, instead of treating the query as isolated syntax.
FAQ
What is the difference between WHERE and HAVING?
WHERE filters rows before grouping. HAVING filters groups after GROUP BY.
Can I use an alias in a WHERE clause?
Usually no, because WHERE is evaluated before SELECT. Use the full expression or wrap the query in a subquery.
What does SELECT * mean?
It returns all columns from the referenced tables. It is convenient for exploration but usually not ideal for production queries.
How do I select data from multiple tables?
Use explicit joins such as INNER JOIN or LEFT JOIN with an ON condition.
What is the default sort direction in ORDER BY?
ASC is the default if you do not specify DESC.
Conclusion
The SQL SELECT statement is much more than a column picker. It is the framework that ties together filtering, sorting, grouping, joins, and subqueries. Once you understand the logical processing order, the rest of SQL becomes easier to reason about.
For next steps, continue with SQL WHERE Clause, SQL Joins Explained, and SQL ORDER BY.