Power BI to Excel: Choose an Export and Check the Rows

TOOLS Published 8 mins read By Leon Wei
Power BI to Excel: Choose an Export and Check the Rows cover image

Quick summary

Summarize this blog with AI

Choose the export by what the recipient needs: a fixed data snapshot, the matrix layout, a refreshable workbook, or a recurring formatted attachment. Then check the file's exact rows and values against the intended filters. A workbook that opens successfully can still contain the wrong population, omit a category, or misrepresent a total.

This guide uses one small Power BI report to produce a filtered Excel snapshot and check its data. The instructions refer to Power BI Desktop and the Power BI service as documented in October 2026; available options depend on permissions, report settings, and your environment. The worked input is entirely fictional.

Choose the output before choosing the button

Ask the recipient what they will do with the file. Someone who needs to reconcile category totals needs a different artifact from someone who needs a printable matrix or a workbook they can refresh next week.

Choose an export by the required recipient outcome
Required resultRoute to evaluateAcceptance check
A fixed table for reconciliationExport summarized data from the visual to a static Excel workbook.Expected category keys, measures, filters, and source-as-of time.
A matrix resembling the reportData with current layout, where supported.Headers, row groups, totals, and readable cells as well as values.
A workbook that reconnects to the modelA live-connected export or Analyze in Excel.Successful refresh under the intended recipient's access.
A recurring formatted Excel attachmentA paginated report with an appropriate subscription or export process.Parameters, delivery, workbook layout, and source freshness.

Summarized export keeps aggregation. Current layout preserves presentation; it does not turn summaries into transactions. Underlying export is model-dependent. Desktop offers summarized CSV export; the service also offers Excel routes. See Microsoft's visual export documentation.

Analyze in Excel and connected exports require suitable permissions, tenant settings, and supported Excel access. Refreshing a workbook, refreshing its semantic model, and loading the original source are separate operations. A connection is useful when refresh is the requirement; it does not preserve an immutable historical snapshot.

For repeated attachments, distinguish report types: the ExportToFile API supports XLSX for paginated reports, not ordinary Power BI reports. A flow using that API does not automatically become a visual-to-Excel exporter.

Build a five-row report with a known answer

In Power BI Desktop, use Enter data or import this CSV and name the table Sales. Set SaleID to Whole number; CustomerID, Region, and Category to Text; and Amount to Fixed decimal number. This single-table example requires no relationships.

SaleID,CustomerID,Region,Category,Amount
101,A,West,Hardware,120
102,B,West,Hardware,80
103,A,West,Services,50
104,C,East,Hardware,300
105,D,West,Services,0

Create these two measures separately:

Revenue = SUM ( Sales[Amount] )

Customers = DISTINCTCOUNT ( Sales[CustomerID] )

Add a table visual with Category, Revenue, and Customers. Apply a visual filter for Region = West. Keep all categories selected and avoid Top N filters, hierarchies, or other selections in this fixture. Use the exact displayed column names above.

Expected West category rows
CategoryRevenueCustomers
Hardware2002
Services502

The selected sales are 101, 102, 103, and 105. Sale 104 is excluded. The zero-amount sale still contributes customer D to Services. Overall revenue is 250, and the overall distinct-customer count is 3: A, B, and D. Adding the category customer counts gives four memberships because A appears twice. If this distinction is unfamiliar, see the worked DAX totals guide.

Export and retain the original snapshot

  1. If authorized, publish this fictional report to a test workspace and open it in the Power BI service.
  2. Confirm the table still has the West filter and the expected two category rows. Record the report, selection, export time, and source-as-of information separately.
  3. Open the visual's export menu, choose summarized data and the static Excel workbook option, and save the result. If the option is unavailable, check the report owner's export settings and your permissions.
  4. Open the workbook and inspect the category rows, values, filter information, and any truncation notice. Retain this original file unchanged.
  5. For the checker below, create a separate review sheet containing only the three exact headers and the two category rows. Exclude filter-description rows, blank framing rows, and grand totals explicitly. Save that sheet as UTF-8, comma-delimited CSV named export.csv. Use plain decimal formatting without currency symbols or thousands separators, preserving the values. The checker expects a period decimal mark and integer customer counts; verify the saved text if your locale uses other separators.

The review copy has this data contract; row order is immaterial:

Category,Revenue,Customers
Hardware,200,2
Services,50,2

Retain the original workbook and its filter context beside this copy. Converting selected cells to CSV is a controlled step for this tiny exercise, not an automated parser for arbitrary workbook layouts. Do not silently delete an unexpected category or change a value to make the check pass.

Check exact rows, not only the grand total

The checker below verifies this fixture's three-column contract, unique category rows, and exact values. It was validated in Python 3.9.6 against 45 passing and failing cases. It should reject Hardware 180 and Services 70 even though those wrong amounts still total 250.

Save the following complete program as check_export.py. It uses only the Python standard library and calculates the expected rows independently from the fictional sales input.

import csv
import re
import sys
from collections import defaultdict
from decimal import Decimal

# Fictional source input; never a live Power BI query.
SALES = [
    (101, 'A', 'West', 'Hardware', '120'),
    (102, 'B', 'West', 'Hardware', '80'),
    (103, 'A', 'West', 'Services', '50'),
    (104, 'C', 'East', 'Hardware', '300'),
    (105, 'D', 'West', 'Services', '0'),
]
HEADERS = ['Category', 'Revenue', 'Customers']


def expected_rows():
    amounts = defaultdict(Decimal)
    customers = defaultdict(set)
    for _, customer, region, category, amount in SALES:
        if region == 'West':
            amounts[category] += Decimal(amount)
            customers[category].add(customer)
    return {k: (amounts[k], len(customers[k])) for k in amounts}


def check_export(path):
    actual = {}
    with open(path, encoding='utf-8-sig', newline='') as handle:
        reader = csv.DictReader(handle, strict=True)
        if reader.fieldnames != HEADERS:
            raise ValueError('Expected exactly Category,Revenue,Customers headers')
        for line, row in enumerate(reader, start=2):
            if None in row or any(row[k] is None for k in HEADERS):
                raise ValueError(f'CSV record {line}: missing or extra cells')
            key = row['Category']
            if not key or key != key.strip():
                raise ValueError(f'CSV record {line}: blank or padded category')
            if key in actual:
                raise ValueError(f'CSV record {line}: duplicate category {key!r}')
            if not re.fullmatch(r'-?(0|[1-9][0-9]*)(\.[0-9]+)?', row['Revenue']):
                raise ValueError(f'CSV record {line}: revenue must be an unformatted decimal')
            if not re.fullmatch(r'0|[1-9][0-9]*', row['Customers']):
                raise ValueError(f'CSV record {line}: customers must be a nonnegative integer')
            actual[key] = (Decimal(row['Revenue']), int(row['Customers']))
    expected = expected_rows()
    missing = sorted(expected.keys() - actual.keys())
    extra = sorted(actual.keys() - expected.keys())
    if missing or extra:
        raise ValueError(f'Wrong categories: missing={missing}, extra={extra}')
    for key in sorted(expected):
        if actual[key] != expected[key]:
            raise ValueError(f'{key}: got {actual[key]}, expected {expected[key]}')
    return len(actual)


if __name__ == '__main__':
    if len(sys.argv) != 2:
        raise SystemExit('Usage: python check_export.py exported.csv')
    try:
        count = check_export(sys.argv[1])
    except (OSError, UnicodeError, csv.Error, ValueError) as exc:
        raise SystemExit(f'FAIL: {exc}')
    print(f'PASS: {count} category rows match the West fixture exactly.')

With export.csv in the same folder, run:

python check_export.py export.csv

The unchanged two-row fixture prints:

PASS: 2 category rows match the West fixture exactly.

A rejected file exits with a nonzero status and an explanatory FAIL: message. The program checks this small teaching contract; use an independently agreed source reference when adapting it to a real report.

Validation scope: this check establishes whether a CSV matches the fictional expected result. It does not establish how Power BI generated the workbook, whether the source was complete, or whether a recipient has appropriate access. Power BI Desktop/Service, native DAX, Excel export or refresh, paginated rendering, and RLS execution were not run for this walkthrough.

Make the failure cases deliberate

Deliberate validation failures
Change in a separate test copyExpected result
Remove Services or repeat Hardware.Fail: missing or duplicate category.
Change Hardware to 180 and Services to 70.Fail despite the unchanged revenue total.
Include East's 300 in Hardware.Fail: the export no longer matches West.
Drop the zero-amount sale before calculating customers.Services incorrectly becomes one customer; fail.
Leave Revenue blank or rename its header.Fail visibly; do not reinterpret missing data as zero.
Produce no category rows.Fail for this fixture, whose expected population is nonempty.

For a real report, agree the expected population independently. An empty result can be valid after a known filter, but a missing source batch can produce the same empty file. Record which case applies before distributing it. Use the metric reconciliation workflow when the report and the source disagree.

Troubleshoot the output you actually received

The matrix looks flattened. Check the selected export route before redesigning measures. If layout matters, inspect a supported current-layout export. For a paginated workbook, Microsoft's Excel renderer guidance explains how report-item boundaries and alignment can create extra columns or merged cells. Inspect the rendered file; an alignment change is not a universal repair.

The workbook is missing rows. Check filters, access, aggregation, and export limits. Published ceilings are not completeness guarantees: static XLSX can contain fewer than its 150,000-row maximum, and current-layout matrices use a 150,000-data-intersection limit. Inspect warnings and compare expected keys, not file size.

The owner gets different data from the recipient. Test with the intended consumer's approved access. Do not grant a broader workspace role just to obtain a matching export. The dynamic RLS guide explains why author access is an unsuitable consumer test. Also consider where a saved snapshot can subsequently be shared.

A scheduled file is stale. Delivery and refresh are separate. Microsoft's subscription documentation states that a Run now test does not refresh the underlying semantic model. Establish source readiness before delivery using the recurring-report release workflow.

FAQ

Can one visual export combine two filtered report tables?

Do not assume it does. Define each output's grain and filters, then choose a deliberately designed combined table, paginated report, or separate extraction process. This walkthrough exports one table and makes no claim to automate multi-visual delivery.

Why not use underlying data for every request?

Choose the grain that answers the request. This fixture needs two category rows; a list of sales would answer a different question. More detail is not automatically the right result.

Does passing the checker mean the report is ready to share?

It means this CSV matches this fixture. A production release also needs the agreed source population, freshness, recipient access, and a usable workbook. Keep those checks distinct so one successful test does not stand in for the others.

Interview Prep

Begin Your SQL, Python, and R Journey

Master 230 interview-style coding questions and build the data skills needed for analyst, scientist, and engineering roles.

Related Articles

All Articles