Contents/ Part XXV Β· Tools & Workflow/ Chapter 154

SQL & Databases

Before an analysis can run in Python, R, or SPSS, the data has to come from somewhere, and that somewhere is almost always a database. SQL is the language for asking databases questions, and it may be the single most durable, widely used skill in all of data work. This chapter teaches it hands-on, across five runnable notebooks.

⏱️ ~30 min read
🐍 5 Notebooks included
πŸ“Š Chapter 154

Ask where the world's data actually lives, and the honest answer is in relational databases, organized into tables and queried with SQL. The language is over fifty years old, it is remarkably stable, and it runs everything from a phone app's local storage to a bank's core systems to the cloud warehouses behind modern analytics. Learn it once and it pays off for a career, because every tool in this part, Python, R, and the commercial packages, ultimately pulls its data with SQL.

πŸ—ƒοΈ
SQL (Structured Query Language) is the standard language for relational databases, which store data in tables of rows and columns linked by keys. It is declarative: you describe the result you want, and the database figures out how to produce it.
🧰
Five runnable notebooks, real SQL

SQL is a doing skill, so this chapter is a five-notebook mini-course: foundations (SELECT/WHERE/ORDER BY), aggregation (GROUP BY), joins, window functions, and subqueries with the handoff to pandas. Every query runs for real against a small shop database loaded into SQLite, which is built into Python, so there is nothing to install. Read a section here, then run its notebook and change the SQL.

1

The Relational Model

A relational database stores data in tables, each row an record and each column a field. The insight that makes it powerful is to split data across tables to avoid repeating it, and to link the tables with keys. A primary key uniquely identifies each row of a table; a foreign key in another table points back to it. Store each customer once, each product once, and let the orders reference them by key.

Four tables, linked by keys customers customer_id (PK) name, region, segment products product_id (PK) product, category, unit_price orders order_id (PK) customer_id (FK) order_date, channel order_items order_item_id (PK) order_id (FK) product_id (FK) quantity, revenue each order → one customer each line → one order each line → one product
The database used in the notebooks. Customers and products are stored once each; orders reference a customer, and each line item references its order and its product. A JOIN follows these key links to reassemble the full picture on demand.

Many database systems speak SQL, and the core language is portable across them. SQLite is a tiny file-based engine built into your phone and into Python (this chapter uses it). PostgreSQL and MySQL are the popular open-source servers. SQL Server and Oracle run large enterprises. And cloud warehouses like BigQuery, Snowflake, and Redshift answer SQL over enormous datasets. The dialects differ at the edges; the SELECT you learn here works in all of them.

2

The Anatomy of a Query

Almost every SQL query is one sentence with up to six clauses, and they are always written in the same order. You will recognize the logic immediately, because it is the same describe-filter-group-sort you have done throughout this book.

the shape of a query
SELECT   columns, and aggregates
FROM     a table (or joined tables)
WHERE    row conditions
GROUP BY columns to summarize by
HAVING   conditions on the groups
ORDER BY a sort key
LIMIT    how many rows to return

There is one surprise worth learning early, and it prevents a lot of confusion: SQL does not run in the order you write it. It reads FROM first to get the rows, filters with WHERE, forms groups, filters those with HAVING, and only then evaluates the SELECT list, sorts, and limits. That is why you can filter groups in HAVING but not yet refer to a SELECT alias there.

Written top-down, but run in this order 1 FROMget rows 2 WHEREfilter rows 3 GROUP BYform groups 4 HAVINGfilter groups 5 SELECTpick columns 6 ORDER BYsort 7 LIMITcut rows this is why WHERE filters rows and HAVING filters the groups that WHERE's survivors form
Knowing the run order explains the rules: WHERE acts before grouping so it cannot see aggregates, while HAVING acts after and is made for them. It is the same split-apply-combine you have used all along.

Aggregation is the workhorse. COUNT, SUM, AVG, MIN, and MAX collapse many rows to one number, and GROUP BY computes that number for each group. If this feels familiar, it should: SELECT category, SUM(revenue) FROM ... GROUP BY category is the exact SQL twin of df.groupby("category").revenue.sum() from the Python chapter.

3

Joins: The Relational Payoff

Splitting data across tables only pays off because you can put it back together. A JOIN matches rows from two tables on a shared key. The revenue-by-region question is impossible from any single table, but a join from line items to orders to customers makes it a few lines of SQL.

Join typeWhat it keeps
INNER JOINOnly rows that match on both sides. The default and most common; drops non-matches.
LEFT JOINEvery row from the left table, plus matches from the right, with NULL where there is none. How you find records with no match, like customers who never ordered.
RIGHT / FULL JOINThe mirror image, and both sides kept. Less common, and some engines (including SQLite historically) lean on LEFT instead.
revenue by region: a three-table join
SELECT   c.region, ROUND(SUM(oi.revenue), 2) AS revenue
FROM     order_items oi
JOIN     orders    o  ON oi.order_id   = o.order_id
JOIN     customers c  ON o.customer_id = c.customer_id
GROUP BY c.region
ORDER BY revenue DESC;

A join is the SQL counterpart of a pandas merge and an R dplyr join: same idea, same shared key, different words. Join to reassemble, then GROUP BY to summarize, and you can answer almost any cross-table business question. The notebooks run this exact query and several more.

4

Window Functions: Summarize Without Collapsing

A GROUP BY reduces each group to a single row, which is exactly what you want for a summary and exactly wrong when you need to keep every row and add a group-wise number beside it. That is what a window function does: it computes an aggregate over a window of rows defined by an OVER clause, but returns a value for every row.

GROUP BY collapses; a window keeps every row GROUP BY: one row out Jan 9,643 Feb 6,707 Mar 11,262 total 27,612 the detail is gone window: same rows + a new column Jan 9,643 Feb 6,707 Mar 11,262 running 9,643 running 16,350 running 27,612 every month still visible SELECT month, revenue, SUM(revenue) OVER (ORDER BY month) AS running_total FROM monthly;
The only new syntax is OVER (...). With it, SUM becomes a running total, RANK numbers rows, PARTITION BY restarts the window per group, and LAG reaches to the previous row for period-over-period change.

Window functions are what let SQL answer questions a spreadsheet user reaches for constantly: rank the products, show each region's top customer, compute a running total, find month-over-month growth. In the notebook these are one clause each. They are also, not coincidentally, the SQL that analysts are most often asked about in interviews.

🐍

The Five-Notebook Mini-Course

Work through these in order. Each loads the shop database into SQLite and runs real SQL, building from a single SELECT up to window functions and the handoff to pandas.

Notebook 1

SQL Foundations

The core sentence: SELECT and FROM, WHERE with comparisons, BETWEEN, IN and LIKE, ORDER BY and LIMIT for top-N, DISTINCT, and computed columns.

Notebook 2

Aggregation & GROUP BY

COUNT, SUM, AVG, MIN, MAX; grouping by one column and several; the difference between WHERE and HAVING; counting distinct values.

Notebook 3

Joins

INNER JOIN on a key, join-then-aggregate for revenue by region and category, LEFT JOIN to find non-matches, and a full four-table join for top customers.

Notebook 4

Window Functions

SUM OVER for running totals and shares, RANK and ROW_NUMBER, PARTITION BY to rank within groups, and LAG for month-over-month growth.

Notebook 5

Subqueries & pandas

Subqueries and CTEs (the WITH clause) for readable complex logic, then the professional handoff: SQL prepares the data, pandas analyzes and plots it.

Solutions

Challenge Solutions

Worked answers to the five practice challenges below, from a filtered SELECT through a CTE handed to pandas.

Real dataset

A four-table shop database. customers (40) and products (12) are lookup tables; orders (600) records who bought when through which channel; and order_items (1,494 lines) holds the quantities, prices, and revenue, linked by order_id and product_id. The notebooks load these four sheets into SQLite and query them. Running the whole course reproduces the shop's year: 114,592 dollars of revenue, an average order of 191 dollars, Equipment the top category at 57,006, North the top region at 43,069, a July revenue peak, and 4 customers who never placed an order.

Every notebook runs on pandas, the built-in sqlite3, and (notebook 5) matplotlib, all available on Colab with nothing to install.

5

SQL in Data Science, Machine Learning & AI

SQL is not a rival to Python and R; it is the stage before them. The professional pattern is to push the heavy joining, filtering, and aggregating into the database, where the data already lives and the engine is optimized for it, then pull a compact, tidy result into pandas or R for modeling and charts. Because you know the pandas verbs, the SQL ones need no new concepts.

In SQLThe pandas you already know
SELECT col1, col2df[["col1", "col2"]]
WHERE x > 100df[df.x > 100] or df.query("x > 100")
GROUP BY regiondf.groupby("region")
JOIN ... ON keydf.merge(other, on="key")
ORDER BY x DESCdf.sort_values("x", ascending=False)
SUM() OVER (...)df.groupby(...).cumsum() and friends

For machine learning, this matters more than it first appears. Real models are trained on features that are almost always engineered in SQL: a customer's total spend, their order count in the last 90 days, their days since last purchase. Computing those at the source and reading the result into a training frame is the daily reality of applied ML. And the modern data stack has only deepened SQL's role: cloud warehouses run SQL over petabytes, and tools like dbt let analysts build tested, version-controlled transformation pipelines in pure SQL, a discipline now called analytics engineering.

Research note

SQL rests on a genuine piece of theory: Edgar F. Codd's relational model (1970), which recast data as mathematical relations and gave querying a rigorous, set-based foundation. That is why the language is declarative, you state what you want and the database's query planner decides how to compute it. Half a century of hardware has changed underneath, yet the same SELECT still runs, because it was built on an abstraction, not an implementation. Few skills in computing have aged so well, which is a strong argument for learning it deeply.

πŸŽ“ Key Takeaways

  • βœ“Most data lives in relational databases, stored as tables linked by primary and foreign keys, and queried with SQL, a fifty-year-old, remarkably durable skill.
  • βœ“Almost every query is one sentence: SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, and it runs FROM-first, not top-down.
  • βœ“Aggregation is split-apply-combine: GROUP BY with COUNT/SUM/AVG is the SQL twin of a pandas groupby; WHERE filters rows, HAVING filters groups.
  • βœ“Joins reassemble split tables on a shared key: INNER keeps matches, LEFT keeps every left row and marks misses with NULL. A join is a pandas merge in different words.
  • βœ“Window functions summarize without collapsing: OVER turns SUM into a running total, adds rankings with RANK, and compares rows with LAG, all while keeping every row.
  • βœ“The pattern is SQL then Python: push joins, filters, and aggregation into the database, then read a tidy result into pandas or R to model and plot.
  • βœ“SQL is central to modern ML and analytics: features are engineered in SQL, warehouses run it at scale, and dbt made it a version-controlled engineering discipline.
6

Practice Challenges

Five exercises, one per notebook area, on the shop database. Full solutions are in the companion solutions notebook.

1

Filter and sort

List the Accessories products priced under 15 dollars, cheapest first.

Hint: WHERE category = 'Accessories' AND unit_price < 15, then ORDER BY.
2

Aggregate with HAVING

Total revenue per product id, keeping only products that brought in more than 8,000 dollars.

Hint: GROUP BY product_id then HAVING SUM(revenue) > 8000.
3

Three-table join

Report revenue by customer segment (Consumer vs Business), joining line items to orders to customers.

Hint: two JOIN ... ON clauses, then GROUP BY c.segment.
4

Window ranking

Rank the channels (web, mobile, store) by total revenue using a window function, keeping the revenue beside the rank.

Hint: aggregate per channel in a CTE, then RANK() OVER (ORDER BY revenue DESC).
5

CTE to pandas

Use a CTE to get revenue per region, read it into pandas, and report each region's share of the total.

Hint: WITH r AS (...), then read_sql_query and a pandas share calculation.
πŸ““

Solutions notebook

All five challenges worked as real SQL against the shop database: a filtered SELECT, an aggregation with HAVING, a three-table join by segment, a window ranking of channels, and a CTE read into pandas for a share calculation.

πŸ““ View Solutions β–Ά Open in Colab ⬇ GitHub
7

Quiz: Test Yourself

Eight questions on the relational model, SELECT, joins, aggregation, and window functions. Answer them, hit Check Answers, and keep refining until you score 100%. Your progress is saved.

➑️
Up next

Databases are where analysts get data; spreadsheets are where many people first meet it. Excel & BI Tools covers the spreadsheet as an analysis tool and the business-intelligence dashboards, Power BI, Tableau, and Looker, that turn queries into shareable visuals. Browse the full Contents for what is published and what is on the way.