POSTGRESQL TUTORIAL

Reading Postgres EXPLAIN plans: Scan Types & Fixes

Learn how to read PostgreSQL EXPLAIN (ANALYZE) output, understand scan types, and identify slow query execution plans.

Starting a Transaction

A transaction wraps multiple database operations into a single unit of work. If one operation fails, the entire transaction is rolled back, preventing partial updates that could leave data in an inconsistent state.

Connect to the bank database:

sudo user postgres psql
\c bank

View the accounts table:

SELECT * FROM accounts;

The table contains Carla, Alice, and Bob. To transfer $200 from Alice to Bob, begin a transaction:

BEGIN;

The prompt shows an asterisk (*), indicating you are inside a transaction.

Updating Accounts Within a Transaction

Deduct $200 from Alice's account:

UPDATE accounts SET balance = balance - 200 WHERE id = 'ACC001';

Add $200 to Bob's account:

UPDATE accounts SET balance = balance + 200 WHERE id = 'ACC002';

Check the current state (still not permanent):

SELECT * FROM accounts;

Committing the Transaction

Make the changes permanent by committing:

COMMIT;

The asterisk disappears from the prompt. Verify the permanent change:

SELECT * FROM accounts;

Rolling Back a Failed Transaction

If an error occurs during a transaction, you cannot commit. Instead, roll back to the stable state as if the transaction never happened.

Start a new transaction:

BEGIN;

Update Alice's account:

UPDATE accounts SET balance = balance - 200 WHERE id = 'ACC001';

Simulate an error by rolling back:

ROLLBACK;

Check that Alice's balance is unchanged:

SELECT * FROM accounts;

ACID Properties

Every PostgreSQL transaction satisfies ACID:

  • Atomic: If one part of the transaction fails, the whole transaction fails.
  • Consistent: Constraints (check, unique, foreign key, primary key) always hold.
  • Isolation: Uncommitted changes are not visible to other transactions.
  • Durable: After a commit, the change persists on disk.

Isolation in Action

Open a second terminal and connect to the same database:

psql
\c bank

In the first terminal, begin a transaction and update Alice's balance without committing:

BEGIN;
UPDATE accounts SET balance = balance - 67 WHERE id = 'ACC001';
SELECT * FROM accounts;

The first terminal shows Alice's updated balance (e.g., 933).

In the second terminal, query the accounts table:

SELECT * FROM accounts;

Alice's balance remains unchanged because the uncommitted draft is isolated.

Commit the transaction in the first terminal:

COMMIT;

Now query again in the second terminal:

SELECT * FROM accounts;

The change is visible and durable.

Attachments

Files for this tutorial — datasets, slides, archives and more.


What's next

You can now read EXPLAIN output, identify slow plans, and fix them. Next up — MVCC, dead tuples, bloat, and autovacuum, so you understand what happens to your table's internals over time.

Start PostgreSQL
Spec 2 CPU / 4 GiB ·Disk 25 GiB
Sign in to launch this environment
Required 1 VM · 2 CPU · 4 GB · 25 GiB disk
Available 1 VM · 1 CPU · 2 GB · 10 GiB disk
Sign in

Suggested tutorials

Keep the momentum going, pick a related topic.