Both functions answer real but different questions. EXTRACT strips the calendar context: month 1 is 'Januaryness', useful for seasonality across all years. date_trunc keeps the timeline: 2025-01 is a point in history, useful for trends. The bug is answering a trend question with the seasonality tool: the 'monthly revenue' report that quietly sums every year's January. The tell: exactly 12 rows out of a multi-year table.
SELECT EXTRACT (month FROM paid_on)AS m, SUM(fee) AS fees FROM gigs GROUP BY m ORDER BY m;
SELECT strftime(date_trunc('month', paid_on), '%Y-%m') AS m, SUM(fee) AS fees FROM gigs GROUP BY date_trunc('month', paid_on) ORDER BY 1;
Grouping by EXTRACT(month) merged every year's January into one row — the two Januaries summed to 250, where date_trunc keeps 2024-01 and 2025-01 as separate months.
date_trunc for timelines; EXTRACT for seasonality, labeled as such; GROUP BY (year, month) as the explicit spelling.