Introduction to SQL
TL;DR — SQL (Structured Query Language) is the standard language for querying relational databases. You use
SELECTto retrieve data,INSERTto add rows,UPDATEto change values, andDELETEto remove them. This lesson walks through your first queries — no database install needed. The engine behind every runnable box on this track is SQLite 3.46.1, and each run starts from an empty database.
What You'll Learn
By the end of this lesson you will build a small table from nothing, put rows into it, and read those rows back — and you will be able to say, before you run anything, exactly what the database is going to print for each statement you wrote. That second half is the part beginners skip and then pay for: SQL answers a question you asked, which is not always the question you meant, and it almost never tells you that it has done so.
This is the first milestone of the warehouse project this track builds toward, called inspecting the dataset. Before anyone can report on a catalogue of products and orders they have to be able to open it, see what columns it holds, and ask a question that the data can actually answer. That is what you practise here. You arrive with no prerequisite at all — this is where the track starts, and everything you need is on the page.
What is SQL?
SQL (Structured Query Language) is a language for managing and querying data held in relational databases — data arranged as tables of rows and columns, where each column has a name and each row is one record.
The code boxes in this lesson are runnable, and they take two clicks rather than one. Each carries an Open in Playground pill; clicking it turns the box into an editor you can change, with a Run Code button underneath. The practice blocks further down the track ask for something first: they show you the code and what it should print, then ask why it fails or what your plan is, and only once you press Commit hypothesis do you get an editor and a Run fix button. Either way your script goes to SQLite 3.46.1, and every run starts from a brand-new empty database. That last part has one consequence worth knowing before your first click: a script that reads a table has to create that table first, in the same script. Nothing survives from the box above.
Key Features
Simple and Declarative
SQL lets you describe what data you want, not how to get it:
-- Get all users from the database
SELECT * FROM users;
Powerful Data Manipulation
SQL provides commands for creating, reading, updating, and deleting data:
-- Create a new table
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price REAL
);
-- Insert data
INSERT INTO products (id, name, price) VALUES (1, 'Laptop', 999.99);
-- Query the data
SELECT id, name, price FROM products ORDER BY id;
Open that fence with its Open in Playground pill, press Run Code, and read the output from the top. The CREATE TABLE printed nothing at all — it changed the database rather than answering a question, and this engine prints nothing for a statement that returns no rows. The INSERT printed 1 row(s) affected. Only the SELECT printed a table: a header line, a rule of dashes, then one row. Three statements, three different kinds of output, and one of them is silence.
Types are advisory here
The price column above is declared REAL, a floating-point number. SQLite does not enforce declared types the way some other engines do — a column's declared type sets its affinity, a preference for how values are stored, and a length like VARCHAR(20) is accepted and then ignored. The lesson on data types goes into what that means in practice. For now, use INTEGER for whole numbers, REAL for measurements, and TEXT for anything else, and be aware that money in a REAL column is subject to ordinary floating-point rounding: this engine prints 2.50 back as 2.5.
Why Learn SQL?
- Data Access: Query and analyze data from any relational database
- Career Value: SQL is one of the most in-demand skills in tech
- Versatility: Used in web development, data science, analytics, and more
- Simplicity: Easy to learn with powerful capabilities
Your First Query
The simplest SQL query selects data without even needing a table:
-- Display a simple message
SELECT 'Hello, SQL!' AS message;
-- Perform calculations
SELECT 2 + 2 AS result;
-- Work with multiple columns
SELECT
'SQL' AS language,
2025 AS year,
'Awesome' AS description;
Modify it twice, and predict before each Run. First, change 2 + 2 to 7 / 2 — this engine divides two whole numbers as whole numbers, so predict whether the answer comes back 3.5 or 3 before you look. Then change it to 7 / 2.0 and Run again: writing one side with a decimal point changes the arithmetic from integer division to floating-point division, and the answer changes with it.
Reading What a Script Prints
A script is a list of statements, and the output is each statement's own output, one after another with no separator between them. Knowing which statements print — and what shape that print takes — is what lets you look at an output pane and tell "it worked" from "it did nothing".
-- Build a tiny table of school supplies
CREATE TABLE supplies (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price REAL,
notes TEXT
);
INSERT INTO supplies VALUES (1, 'Pencil', 2.50, NULL), (2, 'Ruler', 3.0, 'metric');
SELECT id, name, price, notes FROM supplies ORDER BY id;
Predict
The script above has three statements: a CREATE TABLE, one INSERT that carries two rows, and a SELECT. Before you Run it, decide what the whole output pane will contain. Pay attention to the Pencil row, whose price was written 2.50 and whose notes column was left NULL.
CREATE TABLE supplies (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price REAL,
notes TEXT
);
INSERT INTO supplies VALUES (1, 'Pencil', 2.50, NULL), (2, 'Ruler', 3.0, 'metric');
SELECT id, name, price, notes FROM supplies ORDER BY id;Interactive Playground
Practice what you've learned! Edit and run the SQL code below to experiment with your first database.
-- Create a simple table
CREATE TABLE students (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
age INTEGER,
grade TEXT
);
-- Add some students
INSERT INTO students (id, name, age, grade) VALUES (1, 'Alice', 20, 'A');
INSERT INTO students (id, name, age, grade) VALUES (2, 'Bob', 22, 'B');
INSERT INTO students (id, name, age, grade) VALUES (3, 'Charlie', 21, 'A');
-- Query all students
SELECT * FROM students ORDER BY id;
Modify it twice, and predict before each Run. First, add a fourth student with INSERT INTO students (id, name, age, grade) VALUES (4, 'Dana', 23, 'B'); and predict how many 1 row(s) affected lines the pane will now hold before you look — the answer is four, one per INSERT statement. Then change the last line to SELECT name, grade FROM students ORDER BY name; and predict the shape of the table: two columns instead of four, and the rows in alphabetical order by name rather than by id.
Pro Tip: SQL keywords and table or column names are case-insensitive, so
select,SELECTandSeLeCtall work, and so doesFROM STUDENTS. Text values are a different matter and are compared exactly as written, so'A'and'a'are two different grades. Writing keywords in UPPERCASE and names in lowercase is a convention that makes queries easier to read.
Statements Run in Order, and Each One Sees What the Last One Left
A script is not a description of a finished state — it is a sequence of edits, and every statement sees the database exactly as the statement above it left it. A SELECT placed halfway down a script reports what was true halfway down, which may not be what is true at the end.
CREATE TABLE shelf (id INTEGER PRIMARY KEY, item TEXT NOT NULL, units INTEGER NOT NULL);
The table above already exists. The four lines below stock it, check it, stock it again, and list it.
Arrange the code
These four statements put two items on a shelf, with a peek at the shelf partway through. Put them in the order that makes the peek report only the pencil and the final listing report both items. Then answer what the order is really testing: the peek is the same query wherever you put it, so what is it that changes its answer?
SELECT item AS first_item FROM shelf;INSERT INTO shelf VALUES (1, 'Pencil', 40);INSERT INTO shelf VALUES (2, 'Ruler', 12);SELECT id, item, units FROM shelf ORDER BY id;
When a Query Runs Cleanly and Still Answers Wrong
The most common beginner surprise in SQL is not an error message. It is a query that runs, exits cleanly, and returns rows you did not expect — or no rows at all. The routine that catches it is simple and worth doing every time: say how many rows you expect before you Run, compare that to what came back, and when the two differ, find the reason before you change anything.
Debug
This script records four supplies across two shelves and is meant to list everything on shelf A, which is the pencil and the eraser. It runs, exits cleanly, and prints a header with no rows underneath it. Commit a hypothesis about why the filter matched nothing, then fix the script so both shelf A items come back.
CREATE TABLE supplies (id INTEGER PRIMARY KEY, name TEXT NOT NULL, shelf TEXT NOT NULL, units INTEGER NOT NULL);
INSERT INTO supplies VALUES (1,'Pencil','A',40),(2,'Ruler','B',12),(3,'Eraser','A',7),(4,'Folder','B',0);
SELECT id, name, units FROM supplies WHERE shelf = 'a' ORDER BY id;Expected output:
4 row(s) affected id | name | units ----------------- 1 | Pencil | 40 3 | Eraser | 7
Try It Yourself
Here is the build task. The starter creates a supplies table, fills it, and then runs a query that answers the wrong question — it lists everything, in whatever order the database found convenient. Work the three TODOs until it reports only the items actually in stock, busiest first.
Build
The starter lists every supply in an unspecified order. Work TODO 1 through TODO 3 so the result shows only the three columns named, drops the items with no units left, and comes back with the largest stock first, breaking ties by name.
CREATE TABLE supplies (id INTEGER PRIMARY KEY, name TEXT NOT NULL, shelf TEXT NOT NULL, units INTEGER NOT NULL);
INSERT INTO supplies VALUES (1,'Pencil','A',40),(2,'Ruler','B',12),(3,'Eraser','A',7),(4,'Folder','B',0);
-- TODO 1: report only three columns, in this order: name, units, shelf.
-- The id column should not appear in the result at all.
-- TODO 2: leave out anything with no units in stock.
-- The Folder has 0 units and must not appear; the Eraser has 7 and must.
-- TODO 3: sort by units, largest first, breaking ties by name in alphabetical order.
SELECT * FROM supplies;Expected output:
4 row(s) affected name | units | shelf -------------------- Pencil | 40 | A Ruler | 12 | B Eraser | 7 | A
Once it passes, try two variations, predicting the result before each Run. First, change the sort to ORDER BY name ASC alone and predict the new first row — it becomes Eraser, because alphabetical order ignores the stock counts entirely. Then change the filter to WHERE units > 10 and predict how many rows survive: two, because the Eraser's 7 units now fall below the line.
Transfer
Same table definition, a different engine. The column below is declared to hold text of at most three characters and a number with two decimal places, and a row is written that fits neither description. On this lane the row goes in and the reported length is 8. A strict engine such as PostgreSQL, whose documentation says a value too long for a character varying column is an error, would reject that same INSERT. Which statement describes what actually differs?
CREATE TABLE t (id INTEGER PRIMARY KEY, code VARCHAR(3), qty DECIMAL(10,2));
INSERT INTO t VALUES (1, 'ABCDEFGH', 2.50);
SELECT id, code, length(code) AS len, typeof(qty) AS qty_type, qty FROM t ORDER BY id;Capstone milestone
Milestone — inspecting the dataset. The warehouse project you will build over this track starts with a catalogue someone else filled in, and the first real skill is opening it and asking a question it can actually answer. The build task above is that skill in miniature: you looked at what columns exist, decided which ones the question needed, and wrote a query whose result is reproducible. Confirm you can do each of those deliberately rather than by accident.
Hint: You are not building the warehouse yet. This confirms the inspection habit the whole project rests on: before writing a report, know what the table holds and what a correct answer would look like. In the capstone the same habit is what stops a revenue figure from being quietly wrong — a number with no expected value to compare it against cannot be checked by anyone, including you.
- Named the columns the report needs instead of selecting everything with a star
- Wrote down the number of rows expected before pressing Run, and compared it to what came back
- Used a WHERE clause to exclude rows the question does not ask about
- Added an ORDER BY with a tiebreaking second key, so the same query gives the same rows in the same order every time
- Can say which statements in a script print something and which print nothing
Key Takeaways
- SQL is a declarative language for managing and querying relational databases
- Tables store data in rows and columns, similar to spreadsheets but with strict structure
SELECT * FROM tableretrieves all data from a table; a report names its columns insteadINSERT INTOadds new rows, and you define table structure withCREATE TABLE- Statements run top to bottom, and each one sees what the one above it left behind
- Keywords and names are case-insensitive; quoted text values are not
- A query that runs cleanly can still answer the wrong question — predict the row count, then compare
Next Steps
Now that you understand what SQL is and how databases store data, it is time to write your first real queries. The next lesson covers the SELECT statement in depth — choosing specific columns, creating aliases, removing duplicates, and limiting results. SELECT is the foundation of everything you will do in SQL, so mastering it early pays dividends in every lesson that follows.
Keep exploring:
- Continue learning → — work through all 25 SQL lessons, from SELECT basics to query optimization, right in your browser
- SQL Playground — run queries against a live database right in your browser
Next lesson
Selecting Data
Master the SQL SELECT statement to retrieve specific data from your database. Learn column aliases, DISTINCT, LIMIT, and expressions.
20 min