skip to content

What does SET TRANSACTION configure, and when must you issue it?

level: middleimportance: should knowfreq 38%

answer

  1. it configures, it does not read or write
  2. issue it too late and it errors
  3. isolation level plus access mode
  4. START TRANSACTION accepts the same modes

basics

~20 s

SET TRANSACTION sets a transaction's characteristics — its isolation level and its access mode, READ ONLY or READ WRITE — not any data. It must be issued before the transaction executes its first query or data-modifying statement, or the engine rejects it.

solid answer

~50 s

`SET TRANSACTION` configures *characteristics* of a transaction rather than doing any work in it. The two characteristics you actually use are the isolation level, `SET TRANSACTION ISOLATION LEVEL READ COMMITTED` and its siblings, and the access mode, `SET TRANSACTION READ ONLY` or `READ WRITE`. Placement is the part interviewers probe: it has to come **before** the transaction runs any query or data-modifying statement — issue it after your first `SELECT` and PostgreSQL errors out. The same characteristics can be attached to the opening statement instead, which is usually cleaner: `START TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY;`. A `READ ONLY` transaction rejects `INSERT`, `UPDATE`, `DELETE` and DDL against non-temporary tables while allowing `SELECT`, which makes it a cheap guard on reporting and analytics sessions. Engines also expose a session-level default — PostgreSQL spells it `SET SESSION CHARACTERISTICS AS TRANSACTION …`.

code

sql · 5 lines
sql
-- Characteristics first, before the transaction does any work
START TRANSACTION;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ ONLY;
SELECT count(*) FROM orders WHERE placed_at >= DATE '2026-01-01';
COMMIT;

go deeper

for a junior

Recognise the statement and know it configures a transaction rather than touching data, and that it belongs at the very start of the transaction.

for a middle

Name both characteristics it sets, state the placement rule and what breaking it does, and show the equivalent inline form on START TRANSACTION.

for a senior

Explain READ ONLY as an enforced guard you deploy deliberately — reporting connections, dump scripts, side-effect-free paths — and where the default should be configured instead of repeated in SQL.

for a principal

Decide where transaction characteristics are declared across services: pinned in the pool, defaulted per connection role, or set per transaction, and how that policy stays visible rather than being buried in scattered SQL.

## A statement that configures, not one that acts Most SQL statements read or change data. `SET TRANSACTION` does neither: it declares how the transaction it applies to should behave. The standard calls these *transaction characteristics*, and the two that matter in practice are: - **Isolation level** — `SET TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE }`. What each level actually promises about concurrent transactions is a large subject in its own right; the language part is simply that these four names are the standard's spellings and this is the statement that selects one. - **Access mode** — `SET TRANSACTION READ ONLY` or `SET TRANSACTION READ WRITE`. Both can be given at once, comma-separated: `SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ ONLY;`. ## The placement rule The characteristic has to be fixed before the transaction begins doing anything, because the engine needs it in place when the first statement takes its view of the data. So: ```sql START TRANSACTION; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- fine: nothing has run yet SELECT count(*) FROM orders; COMMIT; ``` but: ```sql START TRANSACTION; SELECT count(*) FROM orders; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- ERROR on PostgreSQL ``` PostgreSQL rejects the second form outright, telling you `SET TRANSACTION ISOLATION LEVEL` must be called before any query. There is also a wrinkle in what the statement applies to: the SQL standard frames `SET TRANSACTION` as configuring the *next* transaction, while engines such as PostgreSQL apply it to the current one provided it has not started work. Both readings put the statement in the same place in your script, which is why the difference rarely bites — write it immediately after opening the transaction and you are correct either way. ## The inline form is usually better Every characteristic `SET TRANSACTION` accepts can also be attached to the statement that opens the transaction: ```sql START TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY; ``` This is one statement instead of two, and it makes the placement rule unbreakable — you cannot accidentally put the configuration after a query. Prefer it in application code; keep the standalone `SET TRANSACTION` for interactive sessions and scripts where the opening statement is generated elsewhere. ## What READ ONLY actually does `READ ONLY` is an assertion the engine enforces. Inside a read-only transaction, `SELECT` works normally, but `INSERT`, `UPDATE` and `DELETE` against non-temporary tables, and DDL that changes persistent objects, are rejected with an error at the moment you issue them. It is not a performance hint and not a lock strategy — treat it as a cheap, declarative guard. Typical uses: a reporting or BI connection that must never write, a script that dumps data, a code path you want to prove is side-effect free. Marking such sessions read-only turns "this analytics query accidentally had an UPDATE pasted into it" from an incident into an error message. Some engines can also use the declaration internally to skip work they only need for writers, but the guarantee you should rely on is the enforcement. ## Setting a default for the session Setting characteristics per transaction is explicit but repetitive. Engines expose a session-level default so every subsequent transaction inherits it; PostgreSQL's spelling is `SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ;`, and other engines have their own. Because the spelling varies, application code usually sets this through the connection pool or driver configuration instead of emitting SQL by hand, and reserves the per-transaction statement for the handful of transactions that genuinely need something different from the default. ## What interviewers are checking Three things. That you know `SET TRANSACTION` configures characteristics rather than doing work. That you know the placement rule and can say what happens if you break it. And that you know `READ ONLY` is enforced, not advisory. Being able to add "I usually put the characteristics on `START TRANSACTION` so placement can't go wrong" is the detail that reads as having actually used it.

  • What exactly does a READ ONLY transaction reject?
    `INSERT`, `UPDATE` and `DELETE` against non-temporary tables, and DDL that changes persistent objects. `SELECT` runs normally. The rejection is an error raised when you issue the statement, so it is an enforced guarantee rather than a hint — which is why it works as a guard on reporting connections that must never write.
  • How do you make every transaction in a session use a given isolation level without repeating SET TRANSACTION?
    Set a session-level default. PostgreSQL spells it `SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL …`; other engines have their own spelling, so applications normally configure it through the driver or connection pool instead of emitting SQL. Then use the per-transaction statement only for the transactions that need something different.
  • Why prefer START TRANSACTION with inline characteristics over a separate SET TRANSACTION?
    It removes the failure mode. The characteristics must precede the transaction's first query; putting them on the opening statement makes that structurally impossible to get wrong, and it is one round trip instead of two. The standalone statement is still handy in interactive sessions where the opening statement is issued by something else.

saying these in an interview costs you the question

  • Thinks SET TRANSACTION can change the level mid-transaction
  • Believes READ ONLY is only a performance hint
  • Confuses it with a session-wide setting that persists after COMMIT
  • Assumes it can set things like lock timeouts or autocommit
  • Cannot name the two characteristics it configures

context