Skip to content

Feature/query store group by changer#218

Merged
erikdarlingdata merged 8 commits intoerikdarlingdata:devfrom
rferraton:feature/QueryStore-GroupBy-Changer
Apr 13, 2026
Merged

Feature/query store group by changer#218
erikdarlingdata merged 8 commits intoerikdarlingdata:devfrom
rferraton:feature/QueryStore-GroupBy-Changer

Conversation

@rferraton
Copy link
Copy Markdown
Contributor

What does this PR do?

In the Query Store grid, Add a Group By Selector and the capacity to group (and search) for Top Query (using Query Hash) and Top Module. It build a hierachy from the Group By selection to the QueryId/PlanId.
There is a Group By

  • None that let the grid as is.
  • Query Hash that build a hierarchy with QueryHash > PlanHash > Top/Bottom QueryID+PlanId
  • Module that build a hierarchy with Module> QueryHash > Top/Bottom QueryID+PlanId

Which component(s) does this affect?

  • Desktop App (PlanViewer.App)
  • Core Library (PlanViewer.Core)
  • CLI Tool (PlanViewer.Cli)
  • SSMS Extension (PlanViewer.Ssms)
  • Tests
  • Documentation

How was this tested?

2026-04-12_17h22_21

Describe the testing you've done. Include:

  • Plan files tested : Query Store, Load Plan from query Store
  • Platforms tested : Windows Only

Checklist

  • I have read the contributing guide
  • My code builds with zero warnings (dotnet build -c Debug)
  • All tests pass (dotnet test)
  • I have not introduced any hardcoded credentials or server names

…ed rows in FetchGroupedPlansAsync, the first root row is recursively expanded to the deepest level so the user immediately sees the full hierarchy.

2. Load child plans (LoadHighlightedPlan_Click / LoadSelected_Click / CollectLeafPlans):
• "Load Plan" on a grouped row (QueryHash, PlanHash, or Module level) now recursively collects all descendant leaf plans where both PlanId > 0 and QueryId > 0
• Rows with PlanId == 0 or QueryId == 0 are never loaded
• "Load Selected" does the same expansion for each selected row
3. Golden headers (ApplyGroupByHeaderColors):
• When GroupBy=QueryHash: "Query Hash" and "Plan Hash" column headers get gold (#FFD700) foreground
• When GroupBy=Module: "Module" and "Query Hash" column headers get gold
• When GroupBy=None: all header colors are reset to default
4. Column reorder (ReorderColumnsForGroupBy):
• QueryId and PlanId columns are now pushed after the GroupBy key columns
• QueryHash mode: Expand → Checkbox → QueryHash → PlanHash → QueryId → PlanId → rest
• Module mode: Expand → Checkbox → Module → QueryHash → QueryId → PlanId → rest
• None mode: original column order is fully restored
…nts (query text and plan xml) after computations and top/bottom reduction
…ediate row, the code now picks the QueryText from the top representative leaf row (the one with IsTopRepresentative == true, falling back to the first available leaf) and assigns it to the parent's QueryStorePlan.QueryText. This is done at two levels:

1. Intermediate rows (PlanHash in QueryHash mode, QueryHash in Module mode): Gets the query text from the top representative leaf within that specific group.
2. Root rows (QueryHash level, Module level): Gets the query text from the top representative leaf across all leaves in that root group.
No additional database queries are made — the text comes entirely from grouped.LeafRows which was already fetched.
@erikdarlingdata
Copy link
Copy Markdown
Owner

Nice feature! One small UX thing to consider:

When "Group by Module" is selected and the workload is mostly ad-hoc queries (no stored procedures/functions), the WHERE q.object_id <> 0 filter excludes everything and the user just sees "No Query Store data found for the selected range." — which is technically true for the grouping but could be confusing since there IS data, just not module-scoped data.

Maybe a more specific message like "No module-scoped queries found. Ad-hoc queries are not included in Module grouping." would help users understand what's happening.

…alongside GridLoadingOverlay.IsVisible = true at the start of every fetch, so any lingering empty message is dismissed before the next request.

2. FetchGroupedPlansAsync — When IntermediateRows.Count == 0 and the mode is Module: sets the message text to "No module found in the selected period", shows the overlay, and returns — without writing to StatusText. For QueryHash mode (or any other non-Module grouped mode) that still hits the empty case, the original status bar message is preserved.
@rferraton
Copy link
Copy Markdown
Contributor Author

@erikdarlingdata : Done

image

@erikdarlingdata erikdarlingdata merged commit 59ecc7f into erikdarlingdata:dev Apr 13, 2026
2 checks passed
@erikdarlingdata
Copy link
Copy Markdown
Owner

Thanks for this, Romain! Great feature — the grouped hierarchy with Query Hash and Module modes is really nicely done. Merging now; I'll follow up with a small cleanup pass for a couple of minor things (an extra join and some whitespace).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants