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
Make it your own →
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

  1. 011) Load Monthly P&L Grid
    SELECT * FROM read_csv_auto('QBO_ProfitAndLoss_Month.csv', header=true)
  2. 022) Unpivot Month Columns to Long Form
    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
  3. 033) Roll Up Months to Quarters with Section Subtotals
    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
  4. 044) Quarterly Income Statement Layout
    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 →