Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions src/PlanViewer.App/Controls/QueryStoreGridControl.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,22 +78,34 @@
</StackPanel>
<StackPanel Spacing="4">
<TextBlock Text="Search by" Foreground="{DynamicResource ForegroundBrush}" FontSize="11"/>
<ComboBox x:Name="SearchTypeBox" Width="120" Height="36" FontSize="13"
SelectedIndex="0">
<ComboBox x:Name="SearchTypeBox" Width="140" Height="36" FontSize="13"
SelectedIndex="0" SelectionChanged="SearchType_SelectionChanged">
<ComboBoxItem Content="(none)" Tag=""/>
<ComboBoxItem Content="Query ID" Tag="query-id"/>
<ComboBoxItem Content="Plan ID" Tag="plan-id"/>
<ComboBoxItem Content="Query Hash" Tag="query-hash"/>
<ComboBoxItem Content="Plan Hash" Tag="plan-hash"/>
<ComboBoxItem Content="Module" Tag="module"/>
<ComboBoxItem Content="Execution Type" Tag="execution-type"/>
</ComboBox>
</StackPanel>
<StackPanel Spacing="4">
<StackPanel x:Name="SearchValuePanel" Spacing="4">
<TextBlock Text=" " FontSize="11"/>
<TextBox x:Name="SearchValueBox" Width="200" Height="36" FontSize="13"
Watermark="0x1AB2C3, dbo.MyProc"
KeyDown="SearchValue_KeyDown"/>
</StackPanel>
<StackPanel x:Name="ExecutionTypePanel" Spacing="4" IsVisible="False">
<TextBlock Text="Execution Type" Foreground="{DynamicResource ForegroundBrush}" FontSize="11"/>
<ComboBox x:Name="ExecutionTypeBox" Width="120" Height="36" FontSize="13"
SelectedIndex="0">
<ComboBoxItem Content="(any)" Tag="any"/>
<ComboBoxItem Content="Regular" Tag="Regular"/>
<ComboBoxItem Content="Aborted" Tag="Aborted"/>
<ComboBoxItem Content="Exception" Tag="Exception"/>
<ComboBoxItem Content="Failed" Tag="Failed"/>
</ComboBox>
</StackPanel>
<StackPanel Spacing="4">
<TextBlock Text="Time display" Foreground="{DynamicResource ForegroundBrush}" FontSize="11"/>
<ComboBox x:Name="TimeDisplayBox" Width="100" Height="36" FontSize="13"
Expand Down Expand Up @@ -350,6 +362,7 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Execution Type" Binding="{ReflectionBinding ExecutionTypeDesc}" SortMemberPath="ExecutionTypeDesc" Width="110"/>
</DataGrid.Columns>
</DataGrid>
<!-- Loading overlay -->
Expand Down
35 changes: 33 additions & 2 deletions src/PlanViewer.App/Controls/QueryStoreGridControl.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@ private static QueryStorePlan GroupedRowToPlan(QueryStoreGroupedPlanRow row)
QueryText = row.QueryText,
PlanXml = row.PlanXml,
CountExecutions = row.CountExecutions,
ExecutionTypeDesc = row.ExecutionTypeDesc,
TotalCpuTimeUs = row.TotalCpuTimeUs,
TotalDurationUs = row.TotalDurationUs,
TotalLogicalIoReads = row.TotalLogicalIoReads,
Expand Down Expand Up @@ -602,15 +603,42 @@ private static QueryStorePlan AggregateGroupedRows(List<QueryStoreGroupedPlanRow
AvgPhysicalIoReads = (double)totalPhysReads / safeExecs,
AvgMemoryGrantPages = (double)totalMem / safeExecs,
LastExecutedUtc = lastExec,
ExecutionTypeDesc = rows.FirstOrDefault()?.ExecutionTypeDesc ?? "",
};
}

private void SearchType_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (SearchValuePanel is null || ExecutionTypePanel is null)
return;

var tag = (SearchTypeBox.SelectedItem as ComboBoxItem)?.Tag?.ToString();
var isExecutionType = tag == "execution-type";
SearchValuePanel.IsVisible = !isExecutionType;
ExecutionTypePanel.IsVisible = isExecutionType;
}

private QueryStoreFilter? BuildSearchFilter()
{
var searchType = (SearchTypeBox.SelectedItem as ComboBoxItem)?.Tag?.ToString();
var searchValue = SearchValueBox.Text?.Trim();

if (string.IsNullOrEmpty(searchType) || string.IsNullOrEmpty(searchValue))
if (string.IsNullOrEmpty(searchType))
return null;

if (searchType == "execution-type")
{
var tag = (ExecutionTypeBox.SelectedItem as ComboBoxItem)?.Tag?.ToString();
// "any" tag (first item) means no filter
if (string.IsNullOrEmpty(tag) || tag == "any")
return null;
// "Failed" bundles Aborted + Exception into an IN predicate
if (tag == "Failed")
return new QueryStoreFilter { ExecutionTypeDescs = ["Aborted", "Exception"] };
return new QueryStoreFilter { ExecutionTypeDescs = [tag] };
}

var searchValue = SearchValueBox.Text?.Trim();
if (string.IsNullOrEmpty(searchValue))
return null;

var filter = new QueryStoreFilter();
Expand Down Expand Up @@ -880,6 +908,8 @@ private void ClearSearch_Click(object? sender, RoutedEventArgs e)
{
SearchTypeBox.SelectedIndex = 0;
SearchValueBox.Text = "";
// Resetting SearchTypeBox triggers SearchType_SelectionChanged which hides ExecutionTypePanel.
ExecutionTypeBox.SelectedIndex = 0;
}

private async System.Threading.Tasks.Task LoadTimeSlicerDataAsync(
Expand Down Expand Up @@ -1865,6 +1895,7 @@ public double WaitMaxGrandTotal
public string QueryHash => Plan.QueryHash;
public string QueryPlanHash => Plan.QueryPlanHash;
public string ModuleName => Plan.ModuleName;
public string ExecutionTypeDesc => Plan.ExecutionTypeDesc;

public string ExecsDisplay => Plan.CountExecutions.ToString("N0");
public string TotalCpuDisplay => (Plan.TotalCpuTimeUs / 1000.0).ToString("N0");
Expand Down
1 change: 1 addition & 0 deletions src/PlanViewer.App/Dialogs/QueryStoreHistoryWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
<DataGridTextColumn Header="Min DOP" Binding="{ReflectionBinding MinDop}" Width="70"/>
<DataGridTextColumn Header="Max DOP" Binding="{ReflectionBinding MaxDop}" Width="70"/>
<DataGridTextColumn Header="Last Execution" Binding="{ReflectionBinding LastExecutionLocal}" Width="140"/>
<DataGridTextColumn Header="Execution Type" Binding="{ReflectionBinding ExecutionTypeDesc}" Width="110"/>
</DataGrid.Columns>
</DataGrid>

Expand Down
1 change: 1 addition & 0 deletions src/PlanViewer.Core/Models/QueryStoreGroupBy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class QueryStoreGroupedPlanRow
/// for a query_hash/plan_hash pair. Only meaningful for leaf-level (QueryId/PlanId) rows.
/// </summary>
public bool IsTopRepresentative { get; set; }
public string ExecutionTypeDesc { get; set; } = "";
}

/// <summary>
Expand Down
1 change: 1 addition & 0 deletions src/PlanViewer.Core/Models/QueryStoreHistoryRow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class QueryStoreHistoryRow
public int MinDop { get; set; }
public int MaxDop { get; set; }
public DateTime? LastExecutionUtc { get; set; }
public string ExecutionTypeDesc { get; set; } = "";

// Display-formatted properties (2 decimal places)
public string AvgDurationMsDisplay => AvgDurationMs.ToString("N2");
Expand Down
6 changes: 6 additions & 0 deletions src/PlanViewer.Core/Models/QueryStorePlan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public class QueryStoreFilter
public string? QueryHash { get; set; }
public string? QueryPlanHash { get; set; }
public string? ModuleName { get; set; }
/// <summary>
/// One or more execution_type_desc values to filter by.
/// Single value → equality predicate; multiple values (e.g. "Aborted","Exception" for "Failed") → IN predicate.
/// </summary>
public string[]? ExecutionTypeDescs { get; set; }
}

public class QueryStorePlan
Expand All @@ -22,6 +27,7 @@ public class QueryStorePlan
public string QueryHash { get; set; } = "";
public string QueryPlanHash { get; set; } = "";
public string ModuleName { get; set; } = "";
public string ExecutionTypeDesc { get; set; } = "";
public string QueryText { get; set; } = "";
public string PlanXml { get; set; } = "";

Expand Down
Loading