Mastering Postgres
Introduction
Introduction to the course
Overview of course structure
Postgres vs. everyone
The psql CLI
Data Types
Introduction to schema
Integers
Numeric
Floating point
Storing money
NaNs and infinity
Casting types
Characters types
Check constraints
Domain types
Charsets and collations
Binary data
UUIDs
Boolean
Enums
Timestamps
Timezones
Dates and times
Advanced Data Types
Intervals
Serial type
Sequences
Identity
Network and mac addresses
JSON
Arrays
Generated columns
Text search types
Bit string
Ranges
Composite types
Nulls
Unique constraints
Exclusion constraints
Foreign key constraints
Indexing
Introduction to indexes
Heaps and CTIDs
B-Tree overview
Primary keys vs. secondary indexes
Primary key types
Where to add indexes
Index selectivity
Composite indexes
Composite range
Combining multiple indexes
Covering indexes
Partial indexes
Index ordering
Ordering nulls in indexes
Advanced Indexing
Functional indexes
Duplicate indexes
Hash indexes
Naming indexes
Understanding Query Plans
Introduction to explain
Explain structure
Scan nodes
Costs and rows
Explain analyze
Generating Results
Introduction to queries
Cross joins
Inner joins
Outer joins
Subqueries
Lateral joins
ROWS FROM
Filling gaps in sequences
Subquery elimination
Combining queries
Set generating functions
Indexing joins
Advanced SQL
Introduction to advanced SQL
Grouping
Grouping sets, rollups, cubes
Window functions
CTEs
CTEs with window functions
Recursive CTE
Hierarchical recursive CTE
Handling nulls
Row value syntax
Views
Materialized views
Removing duplicate rows
Upsert
Returning keyword
COALESCE + generated column
Full Text Search
Introduction to full text search
Searching with LIKE
Vectors, queries, and ranks
Websearch
Ranking
Indexing full text search
Highlighting
Advanced JSON
Intro to JSON
JSON vs JSONB
Validating JSON
Creating JSON objects + arrays
JSON extraction
JSON containment
JSON existence
JSON recordset
Updating JSON
Indexing JSON parts
GIN index
Vectors (pgvector)
Intro to pgvector
Vector embedding columns
Find related articles
Upsert vector embedding
Semantic search
Other operators
Vector indexes
Outro
Thank you
Bonus interviews
Heroku's glory days & Postgres vs the world (with Craig Kerstiens)
Creating a Postgres platform with Monica & Tudor from Xata.io
Bootstrapping an email service provider (with Jesse Hanley)
Locked video

Please purchase the course to watch this video.

Video thumbnail
Indexing
Primary keys vs. secondary indexes

Full Course

$
349
$399
USD, one-time fee
Mastering Postgres has dramatically improved my understanding of indexes. It has helped me make informed decisions around my queries and schema rather than blindly adding indexes and hoping for the best.
Dwight Watson
Dwight Watson

PostgreSQL database platform

Shorten dev cycles with branching and zero-downtime schema migrations.

Test Data

I've made my test data available for you to use and follow along.

Download
or
Use on Xata

Summary

In Postgres, all indexes are secondary because data is stored in an unordered heap—there’s no concept of a clustered index like in MySQL. A primary key is simply a special secondary index that enforces uniqueness and not-null constraints while automatically creating the associated index. Unlike clustered storage, Postgres must traverse both the index and the heap to retrieve row data, which influences performance considerations in query planning and indexing strategy.

Video Transcript

We need to cover just a little bit more terminology before we move on and get back to writing some SQL, which I know is the fun part, but this is very important.

In Postgres, every index is in fact a secondary index, which is a little bit wild because we do have primary keys. What is a secondary index? Well, that might be helpful to compare Postgres to MySQL, for example. In MySQL, when you declare a primary key, you are simultaneously declaring the clustered index. In MySQL, the clustered index is the way that the data is arranged on the disk. In MySQL, everything is an index, including the table. The table itself is an index. It is a B-tree index where the entire row is held down in those leaf nodes, those nodes at the very bottom. So that is a clustered index. In MySQL, a secondary index is any other index that is not the clustered index.

Moving back to Postgres, we talked about how the data on the disk is stored in a heap, a big old pile, meaning there is no clustered index. Therefore, every index is a secondary index. Every index lookup requires traversing the index and then hopping over to the heap and finding the rows. That's mostly true. We're gonna get to covering indexes later, and you'll see that there's one caveat to that. In most use cases, you have to traverse the index and then go into the heap. What is a primary key? Well, a primary key is a special type of secondary index. Well, rather it's a secondary index plus plus. A primary key enforces uniqueness, it enforces not nullness, and it automatically creates the underlying index for you. When you declare a column as a primary key, like we looked at in the data types section, it automatically adds not null. It automatically adds unique, and it automatically creates that index for you.

You don't have to create your own index for the primary key that's already there. When you create a table, you can only declare one primary key, kind of in the name, primary, one, only one. You can have as many secondary keys as you want. That's just additional secondary indexes that are not the primary key. It's a little bit esoteric, it's a little bit in the weeds, but there is a difference between the primary key and all of your other indexes. Your other indexes could be not null, they could be unique. That's totally fine, but there is a difference between the primary key. But all indexes in Postgres are in fact secondary indexes.