Analytics report refresh performance degrades significantly after cloud migration

After migrating our D365 Finance environment to cloud, we’re experiencing severe performance degradation with Power BI analytics reports. Reports that previously refreshed in 8-10 minutes now take 45+ minutes, causing significant delays in management reporting.

Our largest report pulls data from multiple fact tables:

SELECT ft.TransDate, ft.Amount, d1.*, d2.*
FROM FactTransaction ft
JOIN DimAccount d1 ON ft.AccountKey = d1.AccountKey
JOIN DimProject d2 ON ft.ProjectKey = d2.ProjectKey
WHERE ft.TransDate >= '2024-01-01'

We’ve noticed that query optimization hasn’t been applied to cloud indexing, and our incremental refresh strategy may not be configured correctly for the cloud environment. The aggregation tables we used on-premises don’t seem to provide the same performance benefits in cloud. Resource monitoring shows high CPU utilization during refresh operations, but we’re not sure how to interpret the cloud-specific metrics. This is impacting our ability to deliver timely financial analytics to executives.

Here’s a comprehensive solution to address all aspects of your Power BI performance degradation:

1. Query Optimization Refactor your SQL query for cloud efficiency:

SELECT
    ft.TransDate,
    ft.Amount,
    d1.AccountNumber,
    d1.AccountName,
    d2.ProjectID,
    d2.ProjectName
FROM FactTransaction ft WITH (NOLOCK)
INNER JOIN DimAccount d1 ON ft.AccountKey = d1.AccountKey
INNER JOIN DimProject d2 ON ft.ProjectKey = d2.ProjectKey
WHERE ft.TransDate >= DATEADD(month, -3, GETDATE())
    AND ft.IsDeleted = 0

Key changes: Specify exact columns needed, use NOLOCK hint for reporting queries, add IsDeleted filter to reduce row scans, limit date range to recent data only.

2. Cloud Indexing Strategy Implement targeted indexes for your query patterns:

  • Create non-clustered index on FactTransaction(TransDate, AccountKey, ProjectKey) INCLUDE (Amount)
  • Index DimAccount(AccountKey) if not already clustered
  • Index DimProject(ProjectKey) if not already clustered
  • Enable automatic index tuning in Azure SQL: ALTER DATABASE SCOPED CONFIGURATION SET AUTOMATIC_TUNING = AUTO
  • Monitor index recommendations in Azure Portal under your SQL database > Performance recommendations

3. Incremental Refresh Strategy Configure Power BI incremental refresh properly:

  • Set refresh range to last 2-3 months (not 12 months)
  • Archive older data: Keep 24 months historical, refresh only recent
  • Use RangeStart and RangeEnd parameters in your Power Query
  • Enable “Detect data changes” using a reliable change tracking column (ModifiedDate)
  • Schedule refreshes during off-peak hours (2-4 AM) when database load is lower

In Power BI Desktop:

  • Create parameters: RangeStart (DateTime), RangeEnd (DateTime)
  • Filter your query: Table.SelectRows(Source, each [TransDate] >= RangeStart and [TransDate] < RangeEnd)
  • Configure incremental refresh policy: Refresh last 3 months, Store 24 months

4. Aggregation Tables Create pre-aggregated tables to reduce computation:

CREATE VIEW vw_MonthlyTransactionSummary AS
SELECT
    DATEADD(month, DATEDIFF(month, 0, ft.TransDate), 0) AS MonthDate,
    ft.AccountKey,
    ft.ProjectKey,
    SUM(ft.Amount) AS TotalAmount,
    COUNT(*) AS TransactionCount
FROM FactTransaction ft
GROUP BY
    DATEADD(month, DATEDIFF(month, 0, ft.TransDate), 0),
    ft.AccountKey,
    ft.ProjectKey

In Power BI, configure aggregation management:

  • Create a separate aggregation table in your model
  • Set up aggregation mappings: Detail table → Aggregation table
  • Power BI will automatically use aggregations when query grain matches
  • Test with Performance Analyzer to verify aggregation usage

5. Resource Monitoring and Scaling Optimize Azure SQL database resources:

  • Upgrade from S3 (100 DTUs) to S6 (400 DTUs) or S7 (800 DTUs) for analytics workload
  • Consider elastic pool if you have multiple databases
  • Enable Query Performance Insight in Azure Portal
  • Set up alerts for DTU usage > 80% sustained
  • Monitor query execution using Azure SQL Analytics workspace

Key metrics to track:

  • DTU percentage (should stay below 80% during refresh)
  • Query duration (identify queries > 30 seconds)
  • Wait statistics (high PAGEIOLATCH indicates I/O bottleneck)
  • Deadlocks and timeouts (should be zero)

6. Power BI Optimization Additional Power BI dataset improvements:

  • Use Import mode instead of DirectQuery where possible for better performance
  • Remove unused columns and tables from your model
  • Disable auto date/time in Power BI Desktop options
  • Optimize DAX measures to use CALCULATE instead of FILTER
  • Enable query folding in Power Query transformations
  • Split large datasets into multiple smaller datasets if needed

7. Implementation Roadmap Phased approach:

  1. Week 1: Implement query optimization and add critical indexes
  2. Week 2: Configure incremental refresh with 3-month window
  3. Week 3: Create and deploy aggregation tables
  4. Week 4: Scale up Azure SQL tier and monitor performance
  5. Week 5: Fine-tune based on Query Performance Insight recommendations

8. Validation and Testing Measure improvements:

  • Baseline current refresh time (45+ minutes)
  • After query optimization: Target 25-30 minutes
  • After incremental refresh: Target 15-20 minutes
  • After aggregations: Target 8-12 minutes
  • After resource scaling: Target 5-8 minutes

Use Power BI Performance Analyzer to identify remaining bottlenecks and continue optimizing. This comprehensive approach should restore and even improve upon your pre-migration performance levels.


This draft is based on general Microsoft Dynamics 365 knowledge. It has not been verified against your specific version and environment. Practitioners: verify the steps and share your experience below.

Cloud SQL databases have different performance characteristics compared to on-premises. Your query is doing full table scans with multiple joins, which is extremely inefficient in Azure SQL. First priority: implement incremental refresh properly. In Power BI, configure your dataset to use incremental refresh with appropriate date range parameters. This will drastically reduce the data volume processed during each refresh. Also, check if your cloud database tier has sufficient DTUs allocated for your workload.

Your query needs serious optimization. You’re selecting all columns from dimension tables (d1., d2.) which is a performance killer. Specify only the columns you actually need. Also, add proper WHERE clause filters at the dimension level, not just on the fact table. For cloud indexing, you need to create non-clustered indexes on the join columns (AccountKey, ProjectKey) and the filter column (TransDate). Check your Azure SQL database’s Query Performance Insight to identify which indexes are missing.

Tested this on our Azure SQL DB migration and replacing SELECT * with explicit columns plus the NOLOCK hint dropped our Power BI report refresh time from 47 minutes to 11 minutes.

Thanks for the suggestions. I’ve checked our Azure SQL tier and we’re on S3 (100 DTUs). Should we be considering a higher tier for our analytics workload? Regarding incremental refresh, we have it enabled but set to refresh the last 12 months. Would reducing this range improve performance? Also, we’re not currently using any aggregation tables in the cloud environment - should we be rebuilding those?

S3 with 100 DTUs is definitely undersized for your workload if you’re processing large fact tables with multiple dimension joins. Consider moving to at least S6 (400 DTUs) or exploring Premium tier options. For incremental refresh, 12 months is quite aggressive - most implementations refresh only the last 1-3 months and archive historical data. Aggregation tables are absolutely critical in cloud environments. Create pre-aggregated views or tables at the grain level you need for your reports. This reduces the computation burden during report refresh significantly.

Don’t forget about Power BI’s built-in aggregation feature. You can create aggregation tables in your dataset that Power BI automatically uses when appropriate. For example, if you have daily transaction data but most reports show monthly summaries, create a monthly aggregation table. Power BI will use the aggregation for summary queries and only hit the detailed table when drill-through is needed. This is a game-changer for cloud performance.

Resource monitoring in Azure is different from on-premises. High CPU during refresh isn’t necessarily bad - it means your database is actively processing. What you should watch for is DTU percentage consistently hitting 100%, which indicates resource starvation. Use Azure SQL Analytics to monitor query duration, wait statistics, and resource consumption patterns. Also enable Query Store in your Azure SQL database to capture actual execution plans and identify problematic queries automatically.