Quarterly Income Statement from QuickBooks
Turns the monthly P&L into a clean quarterly income statement — Income, COGS, Gross Profit, Expenses, Net Income — with a trailing-12-month total column.
4 steps · August 4, 2026
No data is shared in this template. It contains only the recipe — column names and SQL logic. When you run it, your data is processed in your own browser and never leaves your machine.
What data it expects
QBO_ProfitAndLoss_Month.csv
section · VARCHARaccount · VARCHAR2025-08 · DOUBLE2025-09 · DOUBLE2025-10 · DOUBLE2025-11 · DOUBLE2025-12 · DOUBLE2026-01 · DOUBLE2026-02 · DOUBLE2026-03 · DOUBLE2026-04 · DOUBLE2026-05 · DOUBLE2026-06 · DOUBLE2026-07 · DOUBLETotal · DOUBLE
Connect QuickBooks Online or drop a CSV / Excel export with a similar layout — the AI adapts the workflow if your columns differ.
How it works — every step, readable
- 011) Load Monthly P&L Grid
SELECT * FROM read_csv_auto('QBO_ProfitAndLoss_Month.csv', header=true) - 022) Unpivot Month Columns to Long Formbuckets values by condition · filters to the relevant rows · sorts the output
WITH unpivoted AS ( SELECT section, account, period, amount_raw FROM ( UNPIVOT input_1 ON "2025-08", "2025-09", "2025-10", "2025-11", "2025-12", "2026-01", "2026-02", "2026-03", "2026-04", "2026-05", "2026-06", "2026-07" INTO NAME period VALUE amount_raw ) ), parsed AS ( SELECT section, account, period, TRIM(CAST(amount_raw AS VARCHAR)) AS amount_text FROM unpivoted ) SELECT section, account, period, CASE WHEN amount_text LIKE '(%' THEN -1 ELSE 1 END * COALESCE( TRY_CAST( REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(amount_text, '$', ''), ',', ''), '(', ''), ')', ''), ' ', '') AS DOUBLE ), 0 ) AS amount FROM parsed WHERE account IS NOT NULL AND TRIM(CAST(account AS VARCHAR)) <> '' AND account NOT ILIKE '%total%' ORDER BY period, section, account - 033) Roll Up Months to Quarters with Section Subtotalsaggregates rows into summary totals · buckets values by condition · appends result sets (e.g. a TOTAL row)
WITH classified AS ( SELECT CAST(EXTRACT(year FROM STRPTIME(period || '-01', '%Y-%m-%d')) AS VARCHAR) || '-Q' || CAST(EXTRACT(quarter FROM STRPTIME(period || '-01', '%Y-%m-%d')) AS VARCHAR) AS quarter, CASE WHEN section = 'Income' THEN 'Income' WHEN section = 'Cost of Goods Sold' THEN 'Cost of Goods Sold' WHEN section = 'Expenses' THEN 'Expenses' WHEN section = 'Other Income' THEN 'Other Income' WHEN section = 'Other Expenses' THEN 'Other Expenses' ELSE 'Unclassified' END AS section_group, amount FROM input_1 ), section_totals AS ( SELECT quarter, section_group, SUM(amount) AS amount FROM classified WHERE section_group <> 'Unclassified' GROUP BY quarter, section_group ), quarter_lines AS ( SELECT quarter, 10 AS sort_order, 'Income' AS line_item, SUM(CASE WHEN section_group = 'Income' THEN amount ELSE 0 END) AS amount FROM section_totals GROUP BY quarter UNION ALL SELECT quarter, 20 AS sort_order, 'Cost of Goods Sold' AS line_item, SUM(CASE WHEN section_group = 'Cost of Goods Sold' THEN amount ELSE 0 END) AS amount FROM section_totals GROUP BY quarter UNION ALL SELECT quarter, 30 AS sort_order, 'Gross Profit' AS line_item, SUM(CASE WHEN section_group = 'Income' THEN amount ELSE 0 END) - SUM(CASE WHEN section_group = 'Cost of Goods Sold' THEN amount ELSE 0 END) AS amount FROM section_totals GROUP BY quarter UNION ALL SELECT quarter, 40 AS sort_order, 'Expenses' AS line_item, SUM(CASE WHEN section_group = 'Expenses' THEN amount ELSE 0 END) AS amount FROM section_totals GROUP BY quarter UNION ALL SELECT quarter, 50 AS sort_order, 'Net Income' AS line_item, SUM(CASE WHEN section_group IN ('Income', 'Other Income') THEN amount ELSE 0 END) - SUM(CASE WHEN section_group = 'Cost of Goods Sold' THEN amount ELSE 0 END) - SUM(CASE WHEN section_group IN ('Expenses', 'Other Expenses') THEN amount ELSE 0 END) AS amount FROM section_totals GROUP BY quarter ) SELECT quarter, sort_order, line_item, amount FROM quarter_lines ORDER BY quarter, sort_order - 044) Quarterly Income Statement Layoutaggregates rows into summary totals · buckets values by condition · sorts the output
SELECT line_item AS "Line Item", ROUND(SUM(CASE WHEN quarter = '2025-Q3' THEN amount ELSE 0 END), 0) AS "2025-Q3", ROUND(SUM(CASE WHEN quarter = '2025-Q4' THEN amount ELSE 0 END), 0) AS "2025-Q4", ROUND(SUM(CASE WHEN quarter = '2026-Q1' THEN amount ELSE 0 END), 0) AS "2026-Q1", ROUND(SUM(CASE WHEN quarter = '2026-Q2' THEN amount ELSE 0 END), 0) AS "2026-Q2", ROUND(SUM(CASE WHEN quarter = '2026-Q3' THEN amount ELSE 0 END), 0) AS "2026-Q3", ROUND(SUM(amount), 0) AS "Trailing 12 Months" FROM input_1 GROUP BY sort_order, line_item ORDER BY sort_order
Run this on your books
Free to try — no sign-up, no card. The workflow runs in your browser; your data never leaves your machine.
Make it your own →