Skip to content
EL
Contents

SQL data modification

Review a data fix

DecisionFoundation3 min read15 min hands-on#fundamentals#sql#operations

The idea in 30 seconds

One-off UPDATE and DELETE statements run against production data are among the riskiest SQL anyone writes. Review them for exactly which rows they touch: check the WHERE clause's logic, preview the rows with a SELECT, run the change in a transaction, and compare the row count with what you expected before committing.

Helps to know first: SQL basics: reading data

On this page

The situation

Support asks for a clean-up: orders that are still pending or paid but were placed before 1 July 2025 are stale and should be cancelled. An AI assistant writes the statement.

Review taskCancel stale orders

Request: “Cancel pending or paid orders placed before 1 July 2025.”

UPDATE orders SET status = 'cancelled'
WHERE status = 'pending' OR status = 'paid' AND ordered_at < '2025-07-01';

Would you run it? Write the review you would leave.

Your call

How to review a one-off data change

  1. Read the WHERE aloud with brackets. Wherever AND and OR meet, add brackets that show the intended grouping. If the brackets change the meaning, the original was wrong.
  2. Preview with SELECT. Run the same WHERE as a SELECT and look at the rows, not just the count.
  3. Run inside BEGIN … ROLLBACK/COMMIT, with RETURNING. Compare what changed with what you expected, then decide.
  4. Agree the expected row count in advance. Put it in the ticket or the pull request.
  5. Think about what else is affected. Rows in other tables that depend on these ones; caches and search indexes holding the old values; anything downstream that reacts to status changes.

Check yourself

  1. How does PostgreSQL read a OR b AND c?

    Show answer

    As a OR (b AND c), because AND has higher precedence than OR.

  2. What should you do before running a one-off UPDATE on production data?

    Show answer

    Agree the expected rows and count, preview them with a SELECT using the same WHERE, then run the update inside a transaction with RETURNING and commit only if the result matches.

  3. Rewrite status = 'pending' OR status = 'paid' AND ordered_at < '2025-07-01' to mean what support asked for.

    Show answer

    status IN ('pending', 'paid') AND ordered_at < '2025-07-01', or (status = 'pending' OR status = 'paid') AND ordered_at < '2025-07-01'.

Finished reading?

Saved in this browser only.

Sources

Where each claim on this page comes from.

  1. 1.
    PostgreSQL documentation: 4.1 Lexical Structure
    Primary· §4.1.6 Operator Precedence, Table 4.2

    AND has higher precedence than OR, and comparison operators bind more tightly than both.

  2. 2.
    PostgreSQL documentation: 6.2 Updating Data
    Primary· §6.2 Updating Data

    UPDATE changes every row its WHERE clause matches.

  3. 3.
    PostgreSQL documentation: 6.4 Returning Data from Modified Rows
    Primary· §6.4

    RETURNING lists the rows a statement changed.

  4. 4.
    PostgreSQL documentation: 3.4 Transactions
    Primary· §3.4 Transactions

    Changes made inside a transaction can be discarded with ROLLBACK.

  5. 5.
    PostgreSQL documentation: 13.3 Explicit Locking
    Primary· §13.3.2 Row-Level Locks

    UPDATE and DELETE take row-level locks, which are released only at transaction end; a conflicting writer waits for them.

Go further

What to read next, and why it is worth the time.

  1. 1.
    PostgreSQL documentation: 4.1 Lexical Structure
    Primary· §4.1.6 Operator Precedence

    One table that settles every "which part runs first" question about an expression.

Applies to PostgreSQL 18. Draft: not yet fact-checked end to end.