Finance teams are in a peculiar situation. They're the most data-dependent people in the company responsible for revenue recognition, cash flow forecasting, expense tracking, and board reporting yet they're almost always last in the queue when they need a new report from engineering.
The irony is that the data they need is already in the company's databases. Subscription billing, transaction records, expense categories, payroll data, invoice statuses all of it is queryable. The bottleneck isn't the data; it's access.
This article covers the most valuable queries finance teams can run against their databases, how to build reports that refresh automatically, and how to get meaningful numbers without needing SQL expertise or a data analyst on call.
Why Finance Data Lives in Your Database, Not Just Your Accounting Software
Accounting platforms like QuickBooks, Xero, and NetSuite are designed for bookkeeping. They're authoritative for what's been invoiced and paid. But they're deliberately backward-looking and limited in what they can compute on the fly.
The raw transactional data that feeds those platforms or that exists independently in your product database is often far richer:
When you can query these directly, you can answer questions that your accounting software simply can't: "What's our net revenue retention this quarter?" or "Which enterprise customers are at risk of non-renewal based on usage data?"
The Core Financial Queries
These queries target common finance reporting needs. They're written in standard SQL (PostgreSQL-style) adapt field names to your schema.
Monthly Recurring Revenue (MRR)
SELECT
DATE_TRUNC('month', s.started_at) AS month,
SUM(
CASE
WHEN s.billing_interval = 'month' THEN s.amount
WHEN s.billing_interval = 'year' THEN s.amount / 12
ELSE 0
END
) AS mrr
FROM subscriptions s
WHERE s.status = 'active'
AND s.started_at >= '2025-01-01'
GROUP BY DATE_TRUNC('month', s.started_at)
ORDER BY month;Most SaaS teams track this manually in a spreadsheet. Running it directly from your subscriptions table means you get the number as of now, not as of whenever someone last updated the sheet.
MRR Movement (New, Expansion, Contraction, Churn)
WITH mrr_by_customer AS (
SELECT
customer_id,
DATE_TRUNC('month', period_start) AS month,
SUM(amount) AS mrr
FROM subscription_line_items
WHERE status = 'active'
GROUP BY customer_id, DATE_TRUNC('month', period_start)
),
mrr_changes AS (
SELECT
curr.month,
curr.customer_id,
COALESCE(curr.mrr, 0) AS current_mrr,
COALESCE(prev.mrr, 0) AS previous_mrr,
COALESCE(curr.mrr, 0) - COALESCE(prev.mrr, 0) AS change
FROM mrr_by_customer curr
LEFT JOIN mrr_by_customer prev
ON prev.customer_id = curr.customer_id
AND prev.month = curr.month - INTERVAL '1 month'
)
SELECT
month,
SUM(CASE WHEN previous_mrr = 0 AND current_mrr > 0 THEN current_mrr ELSE 0 END) AS new_mrr,
SUM(CASE WHEN change > 0 AND previous_mrr > 0 THEN change ELSE 0 END) AS expansion_mrr,
SUM(CASE WHEN change < 0 AND current_mrr > 0 THEN ABS(change) ELSE 0 END) AS contraction_mrr,
SUM(CASE WHEN current_mrr = 0 AND previous_mrr > 0 THEN previous_mrr ELSE 0 END) AS churned_mrr
FROM mrr_changes
GROUP BY month
ORDER BY month;This is the waterfall view every SaaS CFO needs. Running it from the database means it's always current, not dependent on someone remembering to update it.
Outstanding Invoice Aging
SELECT
customer_name,
invoice_number,
amount_due,
due_date,
CURRENT_DATE - due_date AS days_overdue
FROM invoices
WHERE status IN ('unpaid', 'partial')
AND due_date < CURRENT_DATE
ORDER BY days_overdue DESC;A simple query, but the difference between running this once a week from a live database versus exporting a CSV from your billing tool every time is hours of manual work per month.
Net Revenue Retention (NRR)
WITH cohort_mrr AS (
SELECT
s.customer_id,
DATE_TRUNC('month', s.started_at) AS cohort_month,
SUM(s.amount) AS starting_mrr
FROM subscriptions s
WHERE DATE_TRUNC('month', s.started_at) = '2025-01-01'
GROUP BY s.customer_id, DATE_TRUNC('month', s.started_at)
),
current_mrr AS (
SELECT
s.customer_id,
SUM(s.amount) AS current_mrr
FROM subscriptions s
WHERE s.status = 'active'
AND DATE_TRUNC('month', CURRENT_DATE) = '2026-01-01'
GROUP BY s.customer_id
)
SELECT
ROUND(
SUM(COALESCE(c.current_mrr, 0)) / SUM(cohort.starting_mrr) * 100, 1
) AS nrr_pct
FROM cohort_mrr cohort
LEFT JOIN current_mrr c ON c.customer_id = cohort.customer_id;NRR above 100% means expansion revenue exceeds churn the hallmark of a healthy SaaS business. This number should be in your board slides every quarter, and it should come directly from your database, not from a spreadsheet approximation.
Building Self-Updating Finance Reports
The problem with SQL queries is they require someone to run them. Finance teams typically deal with this in one of three ways:
There's a fourth option: connect your database to a tool that lets non-technical team members ask questions directly, and build dashboards that refresh on a schedule.
With AI for Database, a CFO or controller can type:
"Show me MRR by month for the last 12 months"
or
"Which customers have invoices more than 60 days overdue?"
The tool generates the SQL, runs it against your database, and returns the result as a table or chart. You can then pin that question to a finance dashboard that refreshes every morning no engineer involvement after the initial database connection.
Automating Financial Alerts
Beyond dashboards, finance teams benefit enormously from proactive alerts. Not "check the dashboard" but "the dashboard noticed something and told you."
Useful financial alerts to set up:
Cash flow early warning: Alert when total outstanding receivables exceed your target threshold, or when a single customer's overdue balance crosses a threshold that matters.
MRR drop detection: If monthly recurring revenue contracts more than a set percentage week-over-week, that's an early warning before the end-of-quarter scramble.
Large refund or credit: Alert finance whenever a transaction reversal above a certain amount is recorded useful for fraud detection and for staying on top of exceptions before they become surprises.
Subscription renewal pipeline: Weekly digest of upcoming renewals in the next 30 days with their ARR value so account management knows where to focus.
These alerts don't require DBA access or stored procedures. You define the condition in plain language, connect it to email or Slack, and the system monitors it on your schedule. The alert fires when the condition is true you don't need to check.
Common Finance Reporting Scenarios
Board and Investor Reporting
Board decks typically need: MRR, ARR, YoY growth, gross margin, burn rate, and runway. The first three come from your billing database. If your expense data is also in a queryable form (even a structured CSV in a database table), you can compute gross margin and burn from the same place.
The advantage over spreadsheets: when the board asks "what's this number based on?", you can show them the query. The methodology is transparent and reproducible.
Revenue Recognition Scheduling
For SaaS businesses with annual contracts, revenue recognition means spreading recognized revenue over the contract period. A query like this helps:
SELECT
customer_name,
contract_start,
contract_end,
total_contract_value,
total_contract_value /
(DATE_PART('month', AGE(contract_end, contract_start)) + 1) AS monthly_recognized_revenue
FROM contracts
WHERE status = 'active'
ORDER BY monthly_recognized_revenue DESC;Running this directly means you're always working with current contract data, not a spreadsheet that hasn't been updated since last month's close.
Expense Category Tracking
If your expense management system exports to a database (many do), you can query it with the same approach:
SELECT
category,
SUM(amount) AS total_spend,
COUNT(*) AS transaction_count
FROM expenses
WHERE expense_date >= DATE_TRUNC('month', CURRENT_DATE)
GROUP BY category
ORDER BY total_spend DESC;This gives you current-month spend by category without waiting for a system-generated report.
Getting Finance Teams Access Without Security Risk
A common objection: "We can't give the finance team direct database access what if they accidentally modify something?"
This is a valid concern, but read-only access solves it. Most databases support read-only database users who can SELECT but not INSERT, UPDATE, or DELETE. Create a dedicated read-only credential for finance reporting, and connect that to AI for Database or whatever query tool you use.
Additionally, you can restrict which schemas and tables are visible. Finance doesn't need access to your raw user authentication tables or internal system logs just the billing, subscription, and financial tables. Schema-level permissions let you be precise about what's accessible.
The result: your finance team gets self-serve data access. Engineers stop fielding "can you pull this for me?" requests. And there's no risk of accidental data modification because the credentials don't allow it.
The Practical Path Forward
The goal isn't to replace your accounting system it's to give your finance team direct access to the transactional data that feeds it, so they can answer questions faster, build reports that stay current, and stop depending on engineers for every data pull.
The starting point:
From there, add alerts for the exceptions that matter most overdue invoices, unusual churn, unexpected refunds.
Your database already has the data. The bottleneck is just access. AI for Database gives finance teams that access in plain English, with dashboards that refresh automatically and alerts that surface problems before they reach the board deck.