ExerciseChallenge
Challenge
Eight predicates, and why each plan was chosen
25 minintermediate1–15 yrs
Edge cases. You have to reason, and two valid fixes differ.
What this teaches
- Non-sargable, correctly-costed and stale-statistics are three causes with three fixes
- Filter versus Index Cond in a plan tells you which of the three you have
- Two of the eight have no index problem at all
- An index hint or disabling seqscan treats a cost-model symptom
Starter
Starter.javaOpen in playground
import java.util.*;
/**
* Challenge: eight queries, eight plans. Say why each plan was chosen.
*
* Three causes are in play — the predicate cannot use an index, the optimiser
* costed a scan cheaper, or the statistics are stale — and they need three
* different fixes. Two of the eight have no index problem at all.
*/
public class Starter {
// Schema:
// orders(id bigint pk, status varchar(20), created_at timestamptz,
// customer_id bigint, total numeric)
// Indexes: (status), (created_at), (customer_id), (lower(reference))
// 10 million rows. 60% of them have status = 'CLOSED'.
// ── 1 ── WHERE created_at::date = CURRENT_DATE
// Plan: Seq Scan, Filter: ((created_at)::date = CURRENT_DATE)
// ── 2 ── WHERE status = 'CLOSED'
// Plan: Seq Scan, rows=6000000, actual rows=6000000
// ── 3 ── WHERE status = 'REFUNDED'
// Plan: Seq Scan, rows=2000000, actual rows=1200
// ── 4 ── WHERE customer_id = 90210
// Plan: Index Scan, Index Cond: (customer_id = 90210)
// ── 5 ── WHERE status = 1
// Plan: Seq Scan, Filter: ((status)::integer = 1)
// ── 6 ── WHERE reference ILIKE 'ord-99%'
// Plan: Seq Scan. There is an index on lower(reference).
// ── 7 ── WHERE customer_id = 90210 OR status = 'OPEN'
// Plan: Seq Scan
// ── 8 ── WHERE created_at > now() - interval '1 hour'
// Plan: Index Scan, then 40 seconds spent in a Sort node
public static void main(String[] args) {
// TODO 1: put each of the eight into one bucket — cannot use an index,
// correctly costed scan, or stale statistics. State the evidence from
// the plan, not a guess.
// TODO 2: two plans say "Filter:" on an indexed column. Explain what
// that word means about where the predicate was applied, and why it
// is conclusive.
// TODO 3: number 3 has an estimate off by three orders of magnitude.
// Say what produces that, what to run, and why adding an index would
// not have helped.
// TODO 4: number 5's SQL looks fine. Explain where the cast comes
// from, why it lands on the column rather than the literal, and the
// one-character fix.
// TODO 5: number 6 has an expression index and does not use it.
// Explain the exact-match requirement, then give two fixes and say
// which survives a new call site being written by someone else.
// TODO 6: number 7 cannot use either index. Say why one index cannot
// serve an OR across two columns, and rewrite it so both can be used.
// TODO 7: number 8 uses the index and is still slow. The time is in a
// Sort. Say what would remove it — the answer is about the index
// definition, not the query.
// TODO 8: number 2 is a correctly-costed scan over 60% of the table.
// Give the only two things that would make it faster, and say which
// one makes the selectivity crossover irrelevant.
}
}Run it locally:
cd exercises/java/indexing/index-not-used/02-challenge
javac Starter.java -d /tmp/out && java -cp /tmp/out StarterHints
Hint 1
Sort the eight into three buckets before proposing any fix: the index cannot help, the index could help and was not chosen, and the index is fine and the estimate was wrong.
Hint 2
Two of them show Filter on an indexed column. That single word decides the bucket.
Hint 3
One has an estimate off by two orders of magnitude. The fix is not an index.
Hint 4
One is only slow because of the type of the parameter, and the SQL looks perfectly ordinary.
Done when
- Each of the eight assigned to one of the three causes, with the evidence
- A specific fix per predicate, and no fix repeated where the cause differs
- The type-mismatch one identified, and the reason the cast lands on the column
- The stale-statistics one answered with ANALYZE rather than an index
- You said what forcing the index would prove, and why that is a diagnostic rather than a fix
← Back to Why would the database ignore an index you created?