Here’s a comprehensive solution addressing all the performance bottlenecks:
1. SQL Query Indexing Strategy
Create composite non-clustered indexes to eliminate table scans:
CREATE NONCLUSTERED INDEX IX_ResourceAlloc_Perf ON ResourceAssignment
(ResourceGroupId, StartDate, EndDate) INCLUDE (ResourceId, ProjectId, Hours)
CREATE NONCLUSTERED INDEX IX_ProjectTask_Resource ON ProjectTask
(ProjectId, TaskId) INCLUDE (StartDate, EndDate)
2. Server-Side Pagination Implementation
Modify your grid datasource to implement proper paging. In the form’s data source, set:
- AllowCheck: No
- StartPosition: 0
- MaxAccessRight: Read
- InsertIfEmpty: No
Then override the executeQuery() method to implement ROW_NUMBER() based pagination with OFFSET/FETCH:
SELECT * FROM ResourceAllocationView
ORDER BY StartDate, ResourceId
OFFSET @PageSize * (@PageNum - 1) ROWS
FETCH NEXT @PageSize ROWS ONLY
Set @PageSize to 150 records maximum per grid page.
3. Materialized View for Aggregated Data
Create an indexed view for pre-calculated allocations:
CREATE VIEW vw_ResourceAllocation_Summary WITH SCHEMABINDING AS
SELECT
ResourceId, ProjectId, ResourceGroupId,
DATEADD(week, DATEDIFF(week, 0, StartDate), 0) AS WeekStart,
SUM(AllocatedHours) AS TotalHours,
COUNT_BIG(*) AS RecordCount
FROM dbo.ResourceAssignment
WHERE StartDate >= DATEADD(month, -18, GETDATE())
GROUP BY ResourceId, ProjectId, ResourceGroupId,
DATEADD(week, DATEDIFF(week, 0, StartDate), 0)
CREATE UNIQUE CLUSTERED INDEX IX_ResourceAllocSummary
ON vw_ResourceAllocation_Summary(ResourceId, ProjectId, WeekStart)
4. Grid Performance Tuning
- Enable query hints for read-only scenarios: Add WITH (NOLOCK) to SELECT statements in grid queries
- Implement query timeout handling with retry logic (3 attempts with exponential backoff)
- Add a loading indicator with progress feedback for queries exceeding 2 seconds
- Consider implementing a “Quick View” mode that loads summary data first, then details on demand
5. Hybrid Query Strategy
Implement conditional logic in your data retrieval:
- Date range ≤ 90 days: Query base tables directly (real-time data)
- Date range > 90 days: Query materialized view (aggregated weekly data)
- More than 20 resource groups selected: Force aggregated view regardless of date range
6. Batch Job for View Refresh
Schedule a batch job to refresh the materialized view every 4 hours during business hours:
- Use incremental refresh (only update changed records from last run)
- Run with NOLOCK to avoid blocking operational transactions
- Monitor execution time; should complete in under 5 minutes for 50K records
Expected Results:
- Grid load time: 30+ seconds → 2-4 seconds
- Query timeout errors: Eliminated
- Database deadlocks: Reduced by 95%
- Server CPU during grid operations: Reduced by 60%
Implementation Priority:
- Create indexes immediately (15 min, no downtime)
- Implement server-side pagination (2-3 hours development)
- Create and test materialized view (4-6 hours)
- Deploy hybrid query logic (3-4 hours)
- Set up refresh batch job (1-2 hours)
Test thoroughly in UAT with production-like data volumes before deploying. Monitor SQL Server DMVs (sys.dm_exec_query_stats) post-deployment to verify index usage and query performance improvements.
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.