Write a query that shows this month's revenue next to last month's, so I can see the change.
This is the classic month-over-month comparison. The trick is that a plain self-join on months is fiddly, so the cleanest version computes the totals per month once and then joins the result to itself with a shifted key.
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
order_date TEXT NOT NULL,
amount DECIMAL(10, 2) NOT NULL
);
INSERT INTO orders (id, order_date, amount) VALUES
(1, '2026-01-08', 120.00),
(2, '2026-01-22', 180.00),
(3, '2026-02-05', 240.00),
(4, '2026-02-11', 90.00),
(5, '2026-02-27', 310.00),
(6, '2026-03-03', 205.00),
(7, '2026-03-19', 260.00);
WITH monthly AS (
SELECT strftime('%Y-%m', order_date) AS month,
ROUND(SUM(amount), 2) AS revenue
FROM orders
GROUP BY month
)
SELECT curr.month,
curr.revenue,
prev.revenue AS previous_revenue,
ROUND(curr.revenue - prev.revenue, 2) AS change
FROM monthly AS curr
LEFT JOIN monthly AS prev
ON prev.month = strftime('%Y-%m', date(prev.month || '-01', '-1 month'))
ORDER BY curr.month;Three rows come back. January shows 300.00 with no previous month, February shows 640.00 against January's 300.00 for a change of 340.00, and March shows 465.00 against February's 640.00 for a change of -175.00.
The LEFT JOIN is what makes the first row survivable: January has no predecessor, so prev.revenue is NULL and the change is NULL rather than the row being dropped.
Two practical notes. The month-shifting expression is dialect-specific — SQLite uses date(month, '-1 month'), PostgreSQL uses an interval, MySQL uses DATE_SUB. And on a dataset with gaps, a missing month produces no row at all, so a self-join on the month sequence gives a NULL instead of the previous month's value. If gaps matter, generate a calendar table with every month and left join the revenue onto it, which also gives you zero-revenue months rather than missing ones.