Overview
SELECT names columns, WHERE filters rows, DISTINCT removes duplicates, LIMIT caps output.
On this page7 sections
The table we keep using
Source data
Before running any query, you should know what the source table looks like. Here is a sample of the orders table:
Sample rows from the orders table. Each row is one order placed by a customer.
| order_id | user_id | order_status | order_total | created_at |
|---|---|---|---|---|
| O0001 | U100 | paid | 149.00 | 2026-08-01 09:15:00 |
| O0002 | U101 | cancelled | 42.50 | 2026-08-01 10:30:00 |
| O0003 | U102 | paid | 88.00 | 2026-08-02 14:22:00 |
| O0004 | U100 | pending | 210.75 | 2026-08-03 08:45:00 |
| O0005 | U103 | paid | 55.20 | 2026-08-03 16:10:00 |
SQL (Structured Query Language) is the language you use to ask questions of data stored in tables. Think of a table like a spreadsheet: it has rows (individual records) and columns (properties of each record). When you write a SQL query, you are telling the database exactly which rows and columns you want to see.
The most basic SQL statement is SELECT, which picks which columns to show. FROM tells the database which table to look in. WHERE filters out rows that do not match a condition. Together, these three clauses form the foundation of every SQL query you will ever write.
Formal definition: a SELECT statement is a declarative request that specifies a projection (columns), a source (table), and optional predicates (filters). The database engine decides how to retrieve the data efficiently. You describe what you want, not how to get it.
What we need from it
Imagine you work at an e-commerce company with millions of orders stored in a database. Your manager asks: 'How many orders were cancelled last month?' Without SQL, you would need to export the entire orders table to a spreadsheet and manually filter and count. With SQL, you write a single query that returns the answer in seconds.
Data engineers use SELECT queries to inspect source tables before building pipelines. If you cannot read the raw data correctly, every downstream transformation will be wrong. These basic clauses are your first tool for validating that data loaded correctly, checking for unexpected values, and previewing what a pipeline will produce.
A query without INSERT, UPDATE, or DELETE does not change the table. Reading data is always safe. This makes SELECT the ideal tool for exploration and debugging.
Trace it step by step

SQL is written in a specific order: SELECT, FROM, WHERE, ORDER BY, LIMIT. But the engine processes it differently. It starts with FROM (find the table), then WHERE (filter rows), then SELECT (pick columns), then ORDER BY (sort), then LIMIT (cap rows). Understanding this execution order helps you predict what your query will return.
The engine reads FROM first, then filters, then projects columns, then sorts, then limits.
The warehouse in this tab already holds several tables: orders, order_items, ecommerce_events, payments, and customers. Each table represents a different business entity. The orders table has columns like order_id, user_id, order_status, order_total, and created_at.
Same table, next cut
Example 1: Basic SELECT
The simplest query picks a few columns from a table. The AS keyword is not needed here because we are selecting existing column names directly.
Run the example below in this tab. Read the input, follow the code, then check the output matches what you expect.
SELECT
order_id,
order_status,
order_total
FROM orders;Result: only the three requested columns appear. All rows are included because there is no WHERE.
| order_id | order_status | order_total |
|---|---|---|
| O0001 | paid | 149.00 |
| O0002 | cancelled | 42.50 |
| O0003 | paid | 88.00 |
| O0004 | pending | 210.75 |
| O0005 | paid | 55.20 |
Example 2: Filtering with WHERE
WHERE lets you keep only the rows that match a condition. Text values must be wrapped in single quotes. Numbers do not need quotes.
SELECT order_id, order_total
FROM orders
WHERE order_status = 'cancelled';Result: only rows where order_status equals 'cancelled' survive the filter.
| order_id | order_total |
|---|---|
| O0002 | 42.50 |
You can combine conditions with AND (both must be true) or OR (either can be true). You can also use comparison operators: greater than (>), less than (<), greater than or equal (>=), not equal (<>).
SELECT order_id, order_total, order_status
FROM orders
WHERE order_status = 'paid'
AND order_total > 80;Result: both conditions must be true for a row to appear.
| order_id | order_total | order_status |
|---|---|---|
| O0001 | 149.00 | paid |
| O0003 | 88.00 | paid |
Example 3: Sorting and limiting
ORDER BY sorts the result. DESC means descending (highest first). ASC means ascending (lowest first, and is the default). LIMIT caps how many rows are returned.
SELECT order_id, order_total
FROM orders
WHERE order_status = 'paid'
ORDER BY order_total DESC
LIMIT 3;Result: paid orders sorted from highest to lowest total, capped at 3 rows.
| order_id | order_total |
|---|---|
| O0001 | 149.00 |
| O0003 | 88.00 |
| O0005 | 55.20 |
DISTINCT removes duplicates
DISTINCT removes duplicate result rows. It applies to the entire selected tuple (combination of columns), not just one column. If you SELECT DISTINCT country, device, you get unique country-device pairs.
SELECT DISTINCT order_status
FROM orders
ORDER BY order_status;Result: three distinct statuses exist in the orders table.
| order_status |
|---|
| cancelled |
| paid |
| pending |
What if you forget WHERE?
A common mistake is forgetting to add a WHERE clause when you only need a subset of data. Without WHERE, every row in the table is returned. On a production table with millions of rows, this can be slow and expensive.
-- WRONG: returns ALL orders, not just paid ones
SELECT order_id, order_total
FROM orders;
-- CORRECT: filters to only paid orders
SELECT order_id, order_total
FROM orders
WHERE order_status = 'paid';Three-valued logic with NULL
NULL means 'unknown' or 'missing'. A comparison like order_status = NULL is always unknown (not true or false). Use IS NULL or IS NOT NULL instead. For example: WHERE paid_at IS NULL finds orders that have not been paid.
SELECT * in production
SELECT * returns every column. Fine while learning, but on a 400-column event table it reads fields you never use and wastes resources. Always name the specific columns you need.
No ORDER BY means no guaranteed order
Without ORDER BY, the database can return rows in any order. Parallel execution and storage changes can alter which rows appear first. Always sort when the order matters.
Every SQL query needs at least SELECT and FROM. The others are optional.
| Clause | Question it answers | Required? |
|---|---|---|
| SELECT | Which columns do I want? | Yes, always required |
| FROM | Which table has the data? | Yes, always required |
| WHERE | Which rows match my condition? | No, omit to get all rows |
| DISTINCT | Are there duplicate rows to remove? | No, only when needed |
| ORDER BY | What order should results be in? | No, but recommended |
| LIMIT | How many rows maximum? | No, but useful for previews |
Copy-paste without reading the output
Run Sample first. If the numbers or row count look wrong, stop and re-read the previous section before changing code.
Common beginner questions
What is the difference between single quotes and double quotes in SQL?
Single quotes wrap text values like 'paid'. Double quotes wrap column or table names that have spaces or reserved words. Most of the time, you only need single quotes.
Why does WHERE come before ORDER BY?
The engine filters first (reduces work), then sorts the remaining rows. If you sorted first and filtered second, the engine would waste time sorting rows it will discard.
Can I use LIMIT without ORDER BY?
Yes, but the rows you get back are unpredictable. Without ORDER BY, the database returns whichever rows it finds first, which might change between runs.
What comes next
Now that you can select, filter, and sort individual rows, the next lesson is NULL: missing values, IS NULL, and COALESCE. Aggregations come right after that, and they skip NULL unless you handle it.
Practice
Run Sample to see paid orders sorted by total. Then complete the exercise: return order_id and order_total for cancelled orders, highest total first, at most 10 rows.
Practicals · load into the editor
After you read the theory, run these in the pane on the right. They execute in this tab, no cluster.
Practice this
Same ideas as interview drills. These challenges open in the studio with a dataset and tests already set up.