PostgreSQL is the database behind most serious web and AI applications. This lesson covers the SQL you use every day: querying data, filtering it, and writing new records. Everything hands-on with a real database.
You need two things: the PostgreSQL server and a client to connect to it. We'll use psql (the command line client) and optionally TablePlus or DBeaver for a visual interface.
# macOS
brew install postgresql@16
brew services start postgresql@16
# Ubuntu
sudo apt-get install postgresql postgresql-contrib
sudo systemctl start postgresql
# Connect as the default superuser
psql -U postgres
# Or connect to a specific database
psql -U postgres -d mydb
-- In psql
CREATE DATABASE aiprojects;
-- Connect to it
\c aiprojects
-- Create a table
CREATE TABLE projects (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
status VARCHAR(20) DEFAULT 'active',
created_at TIMESTAMP DEFAULT NOW(),
budget NUMERIC(10,2)
);
-- See the table structure
\d projects
GENERATED ALWAYS AS IDENTITY — same idea, more standard SQL.-- Single row
INSERT INTO projects (name, status, budget)
VALUES ('AI Dashboard', 'active', 5000.00);
-- Multiple rows at once
INSERT INTO projects (name, status, budget)
VALUES
('Chatbot v2', 'planning', 8000.00),
('Data Pipeline', 'active', 12000.00),
('Report Generator', 'complete', 3500.00);
-- Insert and return the new ID
INSERT INTO projects (name, budget)
VALUES ('New Project', 9500.00)
RETURNING id, created_at;
-- All rows, all columns
SELECT * FROM projects;
-- Specific columns
SELECT id, name, status FROM projects;
-- Filter with WHERE
SELECT * FROM projects WHERE status = 'active';
-- Multiple conditions
SELECT * FROM projects
WHERE status = 'active' AND budget > 5000;
-- ORDER and LIMIT
SELECT * FROM projects
ORDER BY budget DESC
LIMIT 5;
-- Pattern matching
SELECT * FROM projects
WHERE name ILIKE '%ai%'; -- case-insensitive LIKE
-- Update specific rows
UPDATE projects
SET status = 'complete', budget = 4200.00
WHERE id = 1;
-- Update with a calculation
UPDATE projects
SET budget = budget * 1.10
WHERE status = 'active';
-- Delete specific rows
DELETE FROM projects WHERE status = 'complete';
-- ALWAYS use WHERE with UPDATE/DELETE
-- Without WHERE you affect every row
UPDATE or DELETE, run the equivalent SELECT with the same WHERE clause to see exactly which rows you're about to modify.SELECT
COUNT(*) AS total_projects,
SUM(budget) AS total_budget,
AVG(budget) AS avg_budget,
MIN(budget) AS min_budget,
MAX(budget) AS max_budget
FROM projects;
-- GROUP BY
SELECT
status,
COUNT(*) AS count,
SUM(budget) AS total_budget
FROM projects
GROUP BY status
ORDER BY total_budget DESC;
learningdb.tasks with columns: id, title, priority (1-5), done (boolean), created_at.CREATE TABLE with data types: SERIAL, VARCHAR, TEXT, INTEGER, NUMERIC, BOOLEAN, TIMESTAMP.INSERT ... RETURNING id gets the auto-generated ID back.WHERE with UPDATE and DELETE — test with SELECT first.COUNT, SUM, AVG, GROUP BY summarize data without loading it into Python.Add a tags column to your table (use TEXT[] — PostgreSQL's array type). Insert rows with multiple tags. Write a query using the @> contains operator to find rows with a specific tag.