📝 SQL Basics — CRUD

CREATE, INSERT, SELECT, UPDATE, DELETE — the five magic words that let you talk to any database.

What is SQL?

SQL stands for Structured Query Language. It is the universal language every relational database understands — PostgreSQL, MySQL, SQL Server, Oracle, SQLite — all of them speak SQL. If databases were countries, SQL would be English: the one language everyone agrees to communicate in.

🧒 ELI5 — Explain Like I'm 5

Imagine you walk into a huge library with millions of books. You can't just grab random shelves — you need to ask the librarian. SQL is the language you use to talk to the librarian (the database). You say things like "Show me all the science fiction books published after 2020" and the librarian fetches exactly what you asked for.

A Tiny Bit of History

In the early 1970s, two researchers at IBM — Edgar F. Codd, Donald Chamberlin, and Raymond Boyce — developed a way to store and retrieve data using simple English-like commands. They called the first version "SEQUEL" (Structured English Query Language). Over time the name was shortened to SQL.

Today, SQL is the most in-demand technical skill in data jobs worldwide. Every data analyst, data engineer, backend developer, and data scientist uses it daily.

50+
Years Old
#1
Data Skill
100%
Relational DBs use it
4
Core Operations

🌍 Real-Life Analogy

Think of SQL as the remote control for your TV. The TV (database) holds thousands of channels (data), but without the remote (SQL), you'd have to walk up and press buttons on the TV itself. SQL lets you sit back and command the database from a distance: "Show me channel 42", "Record this show", "Delete last week's recordings."

SQL Statement Categories

SQL commands are grouped into families. In this lesson we focus on the two you'll use most:

Category Stands For What It Does Example Commands
DDL Data Definition Language Defines the structure of your data CREATE, ALTER, DROP
DML Data Manipulation Language Works with the data itself INSERT, SELECT, UPDATE, DELETE
DCL Data Control Language Controls access & permissions GRANT, REVOKE
TCL Transaction Control Language Manages transactions BEGIN, COMMIT, ROLLBACK

What is CRUD?

CRUD is an acronym that describes the four fundamental things you can do with data:

C
Create — add new data
R
Read — fetch data
U
Update — change data
D
Delete — remove data

🧒 ELI5 — The Phone Contacts App

Open the Contacts app on your phone. Everything you do there maps to CRUD:

  • Create — Tap "Add Contact" and type a new name & number.
  • Read — Search for "Mom" to see her number.
  • Update — Mom changed her number, so you edit it.
  • Delete — Remove that spam caller you accidentally saved.

That's it! Every app — Instagram, Gmail, Amazon, Uber — does these four things under the hood with SQL.

CRUD Mapped to SQL

CRUD Operation SQL Command English Translation
Create INSERT INTO "Add this new row to the table"
Read SELECT "Show me the data that matches..."
Update UPDATE "Change this value to something new"
Delete DELETE "Remove these rows forever"

We also need CREATE TABLE (to build the table in the first place) and ALTER/DROP TABLE (to change or destroy it). This lesson covers all of them.

CREATE TABLE — Building Your First Table

Before you can store any data, you need a table. A table is like a spreadsheet: it has columns (the headers) and rows (the actual data). The CREATE TABLE command defines the columns, what type of data each column holds, and any rules the data must follow.

🌍 Real-Life Analogy — The Blank Spreadsheet

Imagine opening a fresh Excel spreadsheet. Before typing any numbers, you first set up the column headers: "Name", "Department", "Salary", "Hire Date". That's exactly what CREATE TABLE does — it defines the column headers and their rules, but the table starts completely empty.

Syntax

CREATE TABLE table_name (
    column1  DATA_TYPE  constraints,
    column2  DATA_TYPE  constraints,
    ...
);

Our Running Example: employees

Throughout this lesson, we'll work with an employees table. Here's how we create it:

CREATE TABLE employees (
    id          SERIAL        PRIMARY KEY,
    name        VARCHAR(100)  NOT NULL,
    department  VARCHAR(50),
    salary      DECIMAL(10,2),
    hire_date   DATE          DEFAULT CURRENT_DATE
);

Let's break every piece of this down:

SERIAL

This is a PostgreSQL shortcut that means "auto-incrementing integer." Every time you add a new row, PostgreSQL automatically assigns the next number (1, 2, 3, ...). You never have to type the id yourself.

PRIMARY KEY

This marks the column as the unique identifier for each row. No two rows can have the same id. Think of it like a Social Security Number — every person (row) gets a unique one.

VARCHAR(100)

A text column that can hold up to 100 characters. "VARCHAR" stands for "VARiable CHARacters." If you type "Alice" (5 characters), it only stores 5, not 100.

NOT NULL

This means "this column must always have a value." You can't leave it blank. An employee must have a name — it would be weird to have a nameless person on payroll!

DECIMAL(10,2)

A number with up to 10 digits total and 2 decimal places. Perfect for money: 99999999.99 is the max. You don't want salary stored as "around 50k" — you want the exact number.

DEFAULT CURRENT_DATE

If you don't provide a hire_date, PostgreSQL automatically fills in today's date. Defaults save you time and prevent empty fields.

Result: The Empty Table

After running the command above, you get a table with the structure defined but zero rows:

idnamedepartmentsalaryhire_date
(0 rows — the table is empty)

💡 Pro Tip — Naming Conventions

Use lowercase letters and underscores for table and column names. Avoid spaces, special characters, and reserved words. Good: hire_date. Bad: Hire Date, date (reserved word).

INSERT INTO — Adding Data

Now that we have our empty table, let's fill it with data. The INSERT INTO command adds new rows (records) to a table.

🧒 ELI5 — Writing in Your Diary

Imagine you have a brand new diary with pre-printed columns: Date, Mood, What Happened. Every evening, you write a new line. That's INSERT — you're adding a brand-new entry to your diary (table) every time.

Single-Row INSERT

INSERT INTO employees (name, department, salary, hire_date)
VALUES ('Alice Johnson', 'Engineering', 85000.00, '2023-01-15');

Notice we did not provide a value for id. Because it's SERIAL, PostgreSQL auto-assigns 1.

Multi-Row INSERT

You can insert many rows at once by separating each set of values with a comma:

INSERT INTO employees (name, department, salary, hire_date) VALUES
    ('Bob Smith',       'Marketing',    62000.00,  '2023-03-20'),
    ('Carol Davis',     'Engineering',  91000.00,  '2022-11-01'),
    ('David Lee',       'Sales',        55000.00,  '2024-01-10'),
    ('Eva Martinez',    'Engineering',  97000.00,  '2021-07-22'),
    ('Frank Wilson',    'HR',           58000.00,  '2023-06-05'),
    ('Grace Chen',      'Marketing',    67000.00,  '2022-09-14'),
    ('Hank Brown',      'Sales',        52000.00,  '2024-02-28'),
    ('Irene Taylor',    'Engineering',  89000.00,  '2023-04-18'),
    ('Jake Nguyen',     'HR',           61000.00,  '2023-08-30');

After both INSERTs, the table now has 10 employees (Alice + 9 others).

The Table After INSERTs

idnamedepartmentsalaryhire_date
1Alice JohnsonEngineering85000.002023-01-15
2Bob SmithMarketing62000.002023-03-20
3Carol DavisEngineering91000.002022-11-01
4David LeeSales55000.002024-01-10
5Eva MartinezEngineering97000.002021-07-22
6Frank WilsonHR58000.002023-06-05
7Grace ChenMarketing67000.002022-09-14
8Hank BrownSales52000.002024-02-28
9Irene TaylorEngineering89000.002023-04-18
10Jake NguyenHR61000.002023-08-30

What Happens If You Break a Rule?

Try inserting a row without the name column (which is NOT NULL):

INSERT INTO employees (department, salary)
VALUES ('Marketing', 70000);

-- ERROR: null value in column "name" violates not-null constraint

🚨 Important — NOT NULL Means Required

When you define a column as NOT NULL, the database will reject any INSERT that tries to leave it empty. This protects your data quality. Always think about which columns truly need a value.

INSERT With DEFAULT Values

If you skip hire_date, the DEFAULT CURRENT_DATE kicks in:

INSERT INTO employees (name, department, salary)
VALUES ('Karen White', 'Sales', 59000);

-- hire_date will automatically be today's date

SELECT — Reading Data

SELECT is the most important SQL command. You'll use it far more than any other. It retrieves (reads) data from a table. You can fetch everything, fetch specific columns, filter rows, sort results, and much more.

🌍 Real-Life Analogy — Asking the Librarian

Imagine walking into a library with a million books. You don't rummage through every shelf — you go to the librarian and say: "Show me all mystery novels by Agatha Christie published after 1950." The librarian disappears into the stacks and comes back with exactly the books you asked for. That's SELECT — you describe what you want, and the database fetches it.

SELECT * — Get Everything

The asterisk * means "all columns." This fetches every column and every row:

SELECT * FROM employees;

Result: the full 10-row table we saw above. While SELECT * is great for quick exploration, in production code you should always name the columns you need (we'll see why in a moment).

SELECT Specific Columns

Instead of all columns, pick only the ones you need:

SELECT name, department, salary
FROM employees;
namedepartmentsalary
Alice JohnsonEngineering85000.00
Bob SmithMarketing62000.00
Carol DavisEngineering91000.00
David LeeSales55000.00
Eva MartinezEngineering97000.00
Frank WilsonHR58000.00
Grace ChenMarketing67000.00
Hank BrownSales52000.00
Irene TaylorEngineering89000.00
Jake NguyenHR61000.00

💡 Pro Tip — Avoid SELECT * in Production

SELECT * is convenient for quick checks, but in real applications you should always list the columns you need. Why? (1) It's faster — the database doesn't fetch unnecessary data. (2) It's clearer — anyone reading your code knows exactly which columns are used. (3) It's safer — if someone adds a column later, your query won't break.

Column Aliases with AS

You can rename columns in the output using AS:

SELECT
    name        AS employee_name,
    department  AS dept,
    salary      AS annual_pay
FROM employees;
employee_namedeptannual_pay
Alice JohnsonEngineering85000.00
Bob SmithMarketing62000.00
... (8 more rows)

Aliases don't change the actual table — they only change the column names in your query output. They're great for making reports more readable.

ORDER BY — Sorting Results

Want to see the highest-paid employees first?

SELECT name, salary
FROM employees
ORDER BY salary DESC;
namesalary
Eva Martinez97000.00
Carol Davis91000.00
Irene Taylor89000.00
Alice Johnson85000.00
Grace Chen67000.00
Bob Smith62000.00
Jake Nguyen61000.00
Frank Wilson58000.00
David Lee55000.00
Hank Brown52000.00

DESC = descending (biggest first). ASC = ascending (smallest first, which is the default).

LIMIT — Just a Few Rows

When you have millions of rows, you don't want to fetch them all. LIMIT restricts the number of rows returned:

SELECT name, salary
FROM employees
ORDER BY salary DESC
LIMIT 3;
namesalary
Eva Martinez97000.00
Carol Davis91000.00
Irene Taylor89000.00

WHERE — Filtering Data

The WHERE clause filters rows based on conditions. Only rows that match the condition are returned. Without WHERE, you get everything. With WHERE, you get exactly what you need.

🧒 ELI5 — The Picky Eater

Imagine a buffet with 50 dishes. A picky eater doesn't grab everything — they say "Only give me dishes that are vegetarian AND spicy." The WHERE clause is the picky eater. It looks at each row and asks: "Do you meet my criteria?" If yes, you're in. If no, you're skipped.

Basic Comparisons

OperatorMeaningExample
=Equal todepartment = 'Sales'
!= or <>Not equal todepartment != 'HR'
>Greater thansalary > 70000
<Less thansalary < 60000
>=Greater than or equalsalary >= 85000
<=Less than or equalsalary <= 60000

Example: Filter by Department

SELECT name, department, salary
FROM employees
WHERE department = 'Engineering';
namedepartmentsalary
Alice JohnsonEngineering85000.00
Carol DavisEngineering91000.00
Eva MartinezEngineering97000.00
Irene TaylorEngineering89000.00

Example: Filter by Salary

SELECT name, salary
FROM employees
WHERE salary > 80000;
namesalary
Alice Johnson85000.00
Carol Davis91000.00
Eva Martinez97000.00
Irene Taylor89000.00

AND & OR — Combining Conditions

AND means both conditions must be true. OR means at least one must be true.

-- Engineers earning more than 90k
SELECT name, department, salary
FROM employees
WHERE department = 'Engineering'
  AND salary > 90000;
namedepartmentsalary
Carol DavisEngineering91000.00
Eva MartinezEngineering97000.00
-- People in Sales OR HR
SELECT name, department
FROM employees
WHERE department = 'Sales'
   OR department = 'HR';
namedepartment
David LeeSales
Frank WilsonHR
Hank BrownSales
Jake NguyenHR

BETWEEN, IN, LIKE

PostgreSQL gives you handy shortcuts for common filtering patterns:

-- Salaries between 60k and 80k (inclusive)
SELECT name, salary
FROM employees
WHERE salary BETWEEN 60000 AND 80000;

-- Same as: WHERE salary >= 60000 AND salary <= 80000
-- People in specific departments (cleaner than multiple ORs)
SELECT name, department
FROM employees
WHERE department IN ('Engineering', 'Marketing');
-- Names starting with 'A' (pattern matching)
SELECT name
FROM employees
WHERE name LIKE 'A%';

-- % means "any characters after". 'A%' matches Alice, Andrew, Angela...

🌍 Real-Life Analogy — The Library Search

BETWEEN is like telling the librarian: "Show me books published between 2010 and 2020." IN is like saying: "I only want books by Stephen King, J.K. Rowling, or Dan Brown." LIKE is like saying: "Show me any book whose title starts with 'The'."

UPDATE — Changing Existing Data

When data changes in the real world, you need to reflect that in the database. Got a raise? New department? Changed your name? The UPDATE command modifies existing rows.

🌍 Real-Life Analogy — White-Out on Paper

Remember using white-out (correction fluid) on handwritten notes? You'd cover the old text and write the new text on top. UPDATE is the digital version of white-out — it replaces old values with new ones, right where they are. The row stays in the same place; only the data changes.

Syntax

UPDATE table_name
SET column1 = new_value1,
    column2 = new_value2
WHERE condition;

Example: Give Alice a Raise

UPDATE employees
SET salary = 92000.00
WHERE name = 'Alice Johnson';

Before:

idnamesalary
1Alice Johnson85000.00

After:

idnamesalary
1Alice Johnson92000.00

Example: Move Bob to a New Department

UPDATE employees
SET department = 'Sales',
    salary = 65000.00
WHERE name = 'Bob Smith';

You can update multiple columns at once by separating them with commas in the SET clause.

Example: Give Everyone a 10% Raise

UPDATE employees
SET salary = salary * 1.10
WHERE department = 'Engineering';

This multiplies the current salary by 1.10 (a 10% increase) for every engineer. The salary on the right side of = refers to the current value.

🚨 DANGER — UPDATE Without WHERE

If you forget the WHERE clause, every single row in the table gets updated:

-- THIS IS DANGEROUS! Every employee's salary becomes 50000!
UPDATE employees
SET salary = 50000;

Always double-check your WHERE clause before running an UPDATE. A good habit: write the SELECT first to see which rows match, then convert it to an UPDATE.

💡 Pro Tip — Preview Before You Update

Before running a risky UPDATE, run a SELECT with the same WHERE clause to see which rows will be affected:

-- Step 1: Preview (safe)
SELECT * FROM employees WHERE department = 'Engineering';

-- Step 2: If the rows look right, update
UPDATE employees SET salary = salary * 1.10 WHERE department = 'Engineering';

DELETE — Removing Data

DELETE removes rows from a table. This is the "scary" command because once data is deleted, it's gone (unless you have backups or use transactions).

🧒 ELI5 — The Paper Shredder

Imagine you have a filing cabinet full of folders. DELETE is like pulling out one specific folder and feeding it into a paper shredder. It's gone forever. You can't un-shred paper, and you can't un-delete data (without backups). That's why we're extra careful with DELETE.

Syntax

DELETE FROM table_name
WHERE condition;

Example: Remove One Employee

DELETE FROM employees
WHERE name = 'Hank Brown';

This removes only Hank Brown's row. Everyone else stays untouched.

Example: Remove All Employees in a Department

DELETE FROM employees
WHERE department = 'Sales';

This deletes every row where department is 'Sales'. David Lee and Hank Brown (if he hadn't been deleted already) would be removed.

🚨 DANGER — DELETE Without WHERE

Just like UPDATE, forgetting WHERE in a DELETE statement is catastrophic:

-- THIS DELETES EVERY SINGLE ROW in the table!
DELETE FROM employees;

The table structure survives (columns, constraints remain), but all your data is erased. Always include WHERE.

DELETE vs TRUNCATE

If you genuinely want to wipe all rows, PostgreSQL offers TRUNCATE which is faster:

FeatureDELETE FROM tableTRUNCATE TABLE table
Removes rowsOne by one (can use WHERE)All at once (no WHERE)
SpeedSlower for large tablesMuch faster
Can be rolled back?Yes (inside a transaction)Yes in PostgreSQL (not all DBs)
Resets auto-increment?NoYes (with RESTART IDENTITY)
Fires triggers?YesNo

🌍 Real-Life Analogy — Shredder vs Dumpster

DELETE with WHERE is like shredding one specific document. DELETE without WHERE is like shredding every document in the cabinet one by one. TRUNCATE is like tipping the entire filing cabinet into a dumpster — same result (all gone) but way faster.

ALTER TABLE & DROP TABLE

After a table exists, you might realize you forgot a column, or need to rename something, or want to destroy the entire table. That's where ALTER TABLE and DROP TABLE come in.

ALTER TABLE — Add a Column

Oops, we forgot to track email addresses!

ALTER TABLE employees
ADD COLUMN email VARCHAR(255);

This adds a new email column. Existing rows will have NULL for this column until you update them.

ALTER TABLE — Drop a Column

Don't need a column anymore? Remove it:

ALTER TABLE employees
DROP COLUMN email;

🚨 Important — Dropping a Column Deletes Its Data

When you drop a column, all the data in that column vanishes immediately and permanently. This is not recoverable unless you have a backup. Always double-check before dropping columns on production databases.

ALTER TABLE — Rename a Column

ALTER TABLE employees
RENAME COLUMN name TO full_name;

ALTER TABLE — Rename the Table

ALTER TABLE employees
RENAME TO staff;

ALTER TABLE — Change a Column's Type

ALTER TABLE employees
ALTER COLUMN salary TYPE NUMERIC(12,2);

DROP TABLE — The Nuclear Option

DROP TABLE doesn't just delete the data — it destroys the entire table structure. The table ceases to exist. Gone. Columns, rows, constraints, indexes — everything.

-- Destroys the table completely
DROP TABLE employees;

-- Safer version: only drops if the table exists (no error if it doesn't)
DROP TABLE IF EXISTS employees;

🧒 ELI5 — DELETE vs DROP

Think of it this way: DELETE erases the entries you wrote in the notebook but keeps the notebook. DROP TABLE throws the notebook in the fire. The notebook (table) is gone — not just the entries. You'd have to buy a whole new notebook (CREATE TABLE again) to start over.

Quick Reference: ALTER TABLE Commands

ActionSQL Command
Add a columnALTER TABLE t ADD COLUMN col TYPE;
Drop a columnALTER TABLE t DROP COLUMN col;
Rename a columnALTER TABLE t RENAME COLUMN old TO new;
Rename tableALTER TABLE t RENAME TO new_name;
Change column typeALTER TABLE t ALTER COLUMN col TYPE new_type;
Set defaultALTER TABLE t ALTER COLUMN col SET DEFAULT val;
Remove defaultALTER TABLE t ALTER COLUMN col DROP DEFAULT;
Add NOT NULLALTER TABLE t ALTER COLUMN col SET NOT NULL;
Remove NOT NULLALTER TABLE t ALTER COLUMN col DROP NOT NULL;

💡 Pro Tip — IF EXISTS Saves You From Errors

Use DROP TABLE IF EXISTS instead of plain DROP TABLE. If the table doesn't exist, PostgreSQL will silently do nothing instead of throwing an error. Similarly, use ADD COLUMN IF NOT EXISTS (PostgreSQL 9.6+) to avoid duplicate column errors in migration scripts.

The Complete CRUD Cheat Sheet

Let's put it all together. Here's every command from this lesson in one place:

OperationSQL CommandWhat It Does
Create TableCREATE TABLE t (...)Builds a new empty table with defined columns
Create (Insert)INSERT INTO t VALUES (...)Adds one or more new rows
Read (Select)SELECT ... FROM t WHERE ...Fetches rows matching your criteria
UpdateUPDATE t SET col=val WHERE ...Changes values in existing rows
DeleteDELETE FROM t WHERE ...Removes rows permanently
Alter TableALTER TABLE t ...Modifies the table structure
Drop TableDROP TABLE tDestroys the table entirely

🌍 Real-Life Analogy — The Restaurant

Think of a database as a restaurant kitchen:
CREATE TABLE = setting up a new station with labeled containers.
INSERT = stocking ingredients into the containers.
SELECT = the chef checking what ingredients are available.
UPDATE = replacing a stale ingredient with a fresh one.
DELETE = tossing out expired items.
ALTER TABLE = adding a new container to the station.
DROP TABLE = demolishing the entire station.

Practice Exercises

Time to practice! Try these exercises on your own PostgreSQL database. We recommend opening psql or pgAdmin and typing these out manually — don't just copy-paste. Muscle memory matters.

Exercise: Create a Products Table

Create a table called products with these columns:

  • product_id — auto-incrementing primary key
  • product_name — text up to 150 characters, required
  • category — text up to 50 characters
  • price — decimal with 2 decimal places
  • in_stock — boolean, default true

Exercise: Insert 5 Products

Using a single multi-row INSERT, add these products: Laptop ($999.99, Electronics), Notebook ($4.50, Stationery), Coffee Mug ($12.99, Kitchen), USB Cable ($8.75, Electronics), Desk Lamp ($34.00, Furniture).

Exercise: Query Products

Write a SELECT statement that shows only the product_name and price of products in the "Electronics" category, sorted by price from highest to lowest.

Exercise: Update a Price

The Laptop's price dropped to $899.99. Write an UPDATE statement to change it. Make sure you only update the Laptop!

Exercise: Delete Out-of-Stock Items

First, update the USB Cable to in_stock = false. Then, write a DELETE that removes all products where in_stock is false.

Exercise: Alter the Table

Add a new column created_at of type TIMESTAMP with a default of NOW(). Then rename the category column to product_category.

Solution Hints

Try each exercise on your own first! If you're stuck, here are the first two solutions:

-- Exercise 1: Create products table
CREATE TABLE products (
    product_id    SERIAL        PRIMARY KEY,
    product_name  VARCHAR(150)  NOT NULL,
    category      VARCHAR(50),
    price         DECIMAL(10,2),
    in_stock      BOOLEAN       DEFAULT true
);
-- Exercise 2: Insert 5 products
INSERT INTO products (product_name, category, price) VALUES
    ('Laptop',     'Electronics',  999.99),
    ('Notebook',   'Stationery',   4.50),
    ('Coffee Mug', 'Kitchen',      12.99),
    ('USB Cable',  'Electronics',  8.75),
    ('Desk Lamp',  'Furniture',    34.00);

Knowledge Check — Quiz Time!

Test what you've learned! Click the answer you think is correct.

1. What does SQL stand for?

2. Which SQL command is used to add new rows to a table?

3. What happens if you run UPDATE employees SET salary = 0; without a WHERE clause?

4. What is the difference between DELETE and DROP TABLE?

5. What does SERIAL do in a column definition?

6. Which WHERE clause correctly selects rows where salary is between 50000 and 80000?

7. You want to add a new column email to an existing table. Which command do you use?

What's Next?

What You Learned Today

CREATE TABLE
INSERT INTO
SELECT & WHERE
UPDATE
DELETE
ALTER & DROP

You now know the fundamental building blocks of SQL. Every application — from tiny personal projects to billion-dollar platforms like Netflix and Amazon — uses these exact same commands under the hood. The difference is just scale.

🌍 Looking Ahead — Data Types & Constraints

In the next module, we'll dive deeper into Data Types & Constraints. You'll learn about INTEGER vs BIGINT vs SMALLINT, TEXT vs VARCHAR vs CHAR, BOOLEAN, TIMESTAMP, UUID, and the powerful constraint system (UNIQUE, CHECK, FOREIGN KEY) that keeps your data clean and consistent. If this lesson was about learning to speak Database Language, the next one is about expanding your vocabulary!

💡 Pro Tip — Practice Makes Permanent

The best way to learn SQL is by doing. Open psql, create a few tables about something you care about (video games, recipes, your music collection), and practice INSERT, SELECT, UPDATE, and DELETE until they feel second nature. Don't just read — type the commands yourself.

Installation & Setup Data Types & Constraints