ExerciseWarm-up
Warm-up
Enter the index from the wrong side
10 minjunior1–10 yrs
One concept, guided. Near-impossible to fail.
What this teaches
- A composite index is sorted by the first column, then the second
- You may skip columns from the right, never from the left
- (a, b) and (b, a) are different indexes, not two spellings of one
- A full index scan still shows the index in a plan, and is not a seek
Starter
Starter.java
import java.util.*;
/**
* Warm-up: the same two columns, both ways round.
*
* A model, not a database. The only thing modelled is that a composite index
* is sorted by its first column, then its second — which is the whole of the
* leftmost prefix rule.
*/
public class Starter {
static final int ROWS = 1_000_000;
static final int CUSTOMERS = 250_000;
static int customerOf(int row) { return (int) ((row * 7919L) % CUSTOMERS); }
static int statusOf(int row) { return (int) ((row * 31L) % 4); }
/** Leading column in the high bits, so sorting sorts by column order. */
static long[] build(boolean customerFirst) {
long[] e = new long[ROWS];
for (int row = 0; row < ROWS; row++) {
long a = customerFirst ? customerOf(row) : statusOf(row);
long b = customerFirst ? statusOf(row) : customerOf(row);
e[row] = (a << 40) | (b << 20) | row;
}
Arrays.sort(e);
return e;
}
static long lead(long e) { return e >>> 40; }
static long second(long e) { return (e >>> 20) & 0xFFFFF; }
public static void main(String[] args) {
long[] customerStatus = build(true);
int customer = 12_345;
// The seek: binary search on the LEADING column.
int lo = 0, hi = ROWS, comparisons = 0;
while (lo < hi) {
int mid = (lo + hi) >>> 1;
comparisons++;
if (lead(customerStatus[mid]) < customer) lo = mid + 1; else hi = mid;
}
int matched = 0, c = lo;
while (c < ROWS && lead(customerStatus[c]) == customer) { c++; matched++; }
System.out.println("(customer_id, status), WHERE customer_id = ?");
System.out.println(" " + comparisons + " comparisons, " + matched + " entries");
// TODO 1: now try WHERE status = 1 against the SAME index. You cannot
// binary search for it — write the loop that finds the matches and
// count how many entries you had to examine. Predict the number first.
// TODO 2: state the rule you just demonstrated in one sentence, using
// the phrase "leftmost prefix".
// TODO 3: build the reversed index with build(false) and run both
// queries against it. Fill this in:
//
// (customer_id, status) (status, customer_id)
// WHERE customer_id = ? ____ ____
// WHERE status = ? ____ ____
//
// Two of the four are seeks and two are full scans.
// TODO 4: a real database sometimes chooses the full index scan
// anyway, and the plan then names the index. Say why that makes this
// bug harder to spot in a review than a missing index would be.
// TODO 5: which single index would serve BOTH queries with a seek?
// Answer honestly — the answer is not one index.
}
}Run it locally:
cd exercises/java/indexing/composite-index-order/01-warmup
javac Starter.java -d /tmp/out && java -cp /tmp/out StarterDone when
- You seeked on the leading column and counted the comparisons
- You tried the second column alone and counted what it cost
- You built the reversed index and showed which query each one serves
- You can state the leftmost prefix rule in one sentence
← Back to Does an index on (a, b) help a query filtering only on b?