SQLAlchemy Core — SQL as Python
Raw SQL strings are hard to compose and easy to get wrong. SQLAlchemy Core gives you an expression language — write queries as Python objects, get real SQL out.
Why bother
- Type-checked in your editor —
users.c.email == "..."is autocompleted. - Composable — filters, joins, aggregates are Python expressions you can build up conditionally.
- Portable — same code runs against SQLite, Postgres, MySQL, MSSQL.
- No injection — parameters are bound automatically.
Core primitives
create_engine("sqlite:///:memory:")— connection pool + dialect.Table(name, MetaData(), Column(...))— describe your schema.select(t).where(...).order_by(...)— build a SELECT.insert(t)/update(t)/delete(t)— the other DML verbs.with engine.begin() as conn:— auto-commits on success, rolls back on exception.
When to use Core vs the ORM
- Core: ETL, analytics, bulk operations, complex reporting queries.
- ORM (next lesson): object-heavy business logic with rich relationships.
- Both are the same library — you can mix them freely.
Try it
- Add a query that counts users per plan.
- Update Bob's plan to "pro" and re-run the select.
3 graded exercises
Write the code, click Check. We’ll tell you exactly what to fix.
- Exercise 1
Create an in-memory SQLite engine and define a
userstable with two columns:id(Integer, primary key) andemail(String, unique). Then create the table in the database by callingmeta.create_all(engine). - Exercise 2
The engine, metadata, and a
userstable are already set up. Inside awith engine.begin() as conn:block, insert two rows with emails 'alice@x.com' and 'bob@x.com'. Then runselect(users)and print each row so both emails appear in the output. - Exercise 3
The
userstable already has 'alice@x.com' and 'bob@x.com' inserted. Write aselect(users).where(...)query that finds the row where email equals 'alice@x.com', then print only that row'sid.