lesson 69 Performance & query plans · advanced
Partitioning & workload trade-offs
Reason about partitioning choices for different workloads.
the idea
Partitioning splits a large table by a key, typically time, so queries that filter on that key read only matching partitions, and old data can be dropped as whole partitions. Its value depends on queries filtering by the partition key. SQLite has no native partitioning; you can still write the aligned range filters and per-period summaries partitioned engines reward.
Dataset: Energy
The tables the challenges use, with their first 6 rows. Under each column name: the kind of value it holds, and which table it links to.
hourly_demand9 columns · 52,405 rows
| ba_codetext · links to balancing_authorities | hour_ending_utcdate & time · unique id | hour_ending_localdate & time | demand_mwnumber | forecast_mwnumber | local_datedate · links to calendar | hour_numbernumber | adjusted_demand_mwnumber | demand_imputednumber |
|---|---|---|---|---|---|---|---|---|
| BPAT | 2024-01-01 09:00 | 2024-01-01 01:00 | 6302 | 6309 | 2024-01-01 | 1 | 6302 | 0 |
| BPAT | 2024-01-01 10:00 | 2024-01-01 02:00 | 6230 | 6236 | 2024-01-01 | 2 | 6230 | 0 |
| BPAT | 2024-01-01 11:00 | 2024-01-01 03:00 | 6179 | 6265 | 2024-01-01 | 3 | 6179 | 0 |
| BPAT | 2024-01-01 12:00 | 2024-01-01 04:00 | 6197 | 6277 | 2024-01-01 | 4 | 6197 | 0 |
| BPAT | 2024-01-01 13:00 | 2024-01-01 05:00 | 6378 | 6394 | 2024-01-01 | 5 | 6378 | 0 |
| BPAT | 2024-01-01 14:00 | 2024-01-01 06:00 | 6607 | 6625 | 2024-01-01 | 6 | 6607 | 0 |
- ba_codetext · links to balancing_authorities
- BPAT
- hour_ending_utcdate & time · unique id
- 2024-01-01 09:00
- hour_ending_localdate & time
- 2024-01-01 01:00
- demand_mwnumber
- 6302
- forecast_mwnumber
- 6309
- local_datedate · links to calendar
- 2024-01-01
- hour_numbernumber
- 1
- adjusted_demand_mwnumber
- 6302
- demand_imputednumber
- 0
- ba_codetext · links to balancing_authorities
- BPAT
- hour_ending_utcdate & time · unique id
- 2024-01-01 10:00
- hour_ending_localdate & time
- 2024-01-01 02:00
- demand_mwnumber
- 6230
- forecast_mwnumber
- 6236
- local_datedate · links to calendar
- 2024-01-01
- hour_numbernumber
- 2
- adjusted_demand_mwnumber
- 6230
- demand_imputednumber
- 0
- ba_codetext · links to balancing_authorities
- BPAT
- hour_ending_utcdate & time · unique id
- 2024-01-01 11:00
- hour_ending_localdate & time
- 2024-01-01 03:00
- demand_mwnumber
- 6179
- forecast_mwnumber
- 6265
- local_datedate · links to calendar
- 2024-01-01
- hour_numbernumber
- 3
- adjusted_demand_mwnumber
- 6179
- demand_imputednumber
- 0
- ba_codetext · links to balancing_authorities
- BPAT
- hour_ending_utcdate & time · unique id
- 2024-01-01 12:00
- hour_ending_localdate & time
- 2024-01-01 04:00
- demand_mwnumber
- 6197
- forecast_mwnumber
- 6277
- local_datedate · links to calendar
- 2024-01-01
- hour_numbernumber
- 4
- adjusted_demand_mwnumber
- 6197
- demand_imputednumber
- 0
- ba_codetext · links to balancing_authorities
- BPAT
- hour_ending_utcdate & time · unique id
- 2024-01-01 13:00
- hour_ending_localdate & time
- 2024-01-01 05:00
- demand_mwnumber
- 6378
- forecast_mwnumber
- 6394
- local_datedate · links to calendar
- 2024-01-01
- hour_numbernumber
- 5
- adjusted_demand_mwnumber
- 6378
- demand_imputednumber
- 0
- ba_codetext · links to balancing_authorities
- BPAT
- hour_ending_utcdate & time · unique id
- 2024-01-01 14:00
- hour_ending_localdate & time
- 2024-01-01 06:00
- demand_mwnumber
- 6607
- forecast_mwnumber
- 6625
- local_datedate · links to calendar
- 2024-01-01
- hour_numbernumber
- 6
- adjusted_demand_mwnumber
- 6607
- demand_imputednumber
- 0
first 6 of 52,405 rows · your queries use all of them
worked example
Run these steps in order to see the idea before you write your own query. The step marked “the pattern” is what the challenges ask for. Running the example does not count toward progress.
Count rows per month slice. Grouping hour_ending_utc by month shows how a time-partitioned table would be split—each month is one logical partition of the hourly data.the pattern
example.sqlSELECT substr(hour_ending_utc, 1, 7) AS month, COUNT(*) AS rows_in_partition FROM hourly_demand GROUP BY month;
Filter one authority and one month. PJM demand for March 2024 with a half-open range on hour_ending_utc matches how partitioned engines read only the partitions you need.
step-2.sqlSELECT COUNT(*) AS hours FROM hourly_demand WHERE ba_code = 'PJM' AND hour_ending_utc >= '2024-03-01' AND hour_ending_utc < '2024-04-01';
common mistake
A filtered result in SQLite is not evidence of partition elimination. Queries that do not filter on the partition key read every partition.