diff --git a/DBADash/Messaging/QueryStoreTopQueriesMessage.cs b/DBADash/Messaging/QueryStoreTopQueriesMessage.cs index fc117032..097f24ed 100644 --- a/DBADash/Messaging/QueryStoreTopQueriesMessage.cs +++ b/DBADash/Messaging/QueryStoreTopQueriesMessage.cs @@ -51,11 +51,12 @@ public enum QueryStoreGroupByEnum query_id, plan_id, query_hash, - query_plan_hash + query_plan_hash, + object_id } public bool NearestInterval { get; set; } = true; - + private async Task GetIdleSchedulerCount(string connectionString) { await using var cn = new SqlConnection(connectionString); @@ -72,9 +73,9 @@ private async Task GetThreadCountBasedOnSchedulerAvailability(string connec try { idleSchedulerCount = await GetIdleSchedulerCount(connectionString); - Log.Debug("Idle scheduler count {idleSchedulerCount}",idleSchedulerCount); + Log.Debug("Idle scheduler count {idleSchedulerCount}", idleSchedulerCount); } - catch(Exception ex) + catch (Exception ex) { Log.Error(ex, "Error getting idle scheduler count"); } @@ -137,7 +138,7 @@ public override async Task Process(CollectionConfig cfg, Guid handle) { databases = new List { DatabaseName }; } - Log.Debug("Collecting Query Store for databases: {databases} for message {handle}", databases,handle); + Log.Debug("Collecting Query Store for databases: {databases} for message {handle}", databases, handle); var threadCount = 1; switch (databases.Count) { @@ -145,15 +146,15 @@ public override async Task Process(CollectionConfig cfg, Guid handle) throw new Exception("No databases found with Query Store enabled"); case 1: break; + default: threadCount = await GetThreadCountBasedOnSchedulerAvailability(src.SourceConnection.ConnectionString); - Log.Information("Processing message {handle} with thread count {maxDegreeOfParallelism}",handle, threadCount); + Log.Information("Processing message {handle} with thread count {maxDegreeOfParallelism}", handle, threadCount); break; } var resultTable = new DataTable(); - - - var options = new ParallelOptions { MaxDegreeOfParallelism = threadCount}; + + var options = new ParallelOptions { MaxDegreeOfParallelism = threadCount }; // Use a concurrent bag to collect DataTables from parallel tasks. var dataTables = new ConcurrentBag(); @@ -236,7 +237,7 @@ private async Task GetTopQueriesForDatabase(string connectionString, { await using var cn = new SqlConnection(connectionString); await cn.OpenAsync(); - await using var cmd = new SqlCommand(SqlStrings.QueryStoreTopQueries, cn) {CommandTimeout = Lifetime}; + await using var cmd = new SqlCommand(SqlStrings.QueryStoreTopQueries, cn) { CommandTimeout = Lifetime }; cmd.Parameters.AddWithValue("@Database", db); cmd.Parameters.AddWithValue("@FromDate", From); cmd.Parameters.AddWithValue("@ToDate ", To); @@ -246,9 +247,9 @@ private async Task GetTopQueriesForDatabase(string connectionString, cmd.Parameters.AddWithValue("@ObjectID", ObjectID is > 0 ? ObjectID.Value : DBNull.Value); cmd.Parameters.AddWithValue("@ObjectName", string.IsNullOrEmpty(ObjectName) ? DBNull.Value : ObjectName); cmd.Parameters.AddWithValue("@GroupBy", GroupBy.ToString()); - var pQueryHash = cmd.Parameters.Add("@QueryHash", SqlDbType.Binary, 8); + var pQueryHash = cmd.Parameters.Add("@QueryHash", SqlDbType.Binary, 8); pQueryHash.Value = QueryHash is not null ? QueryHash : DBNull.Value; - var pPlanHash = cmd.Parameters.Add("@QueryPlanHash", SqlDbType.Binary,8); + var pPlanHash = cmd.Parameters.Add("@QueryPlanHash", SqlDbType.Binary, 8); pPlanHash.Value = QueryPlanHash is not null ? QueryPlanHash : DBNull.Value; cmd.Parameters.AddWithValue("@QueryID", QueryID is > 0 ? QueryID.Value : DBNull.Value); cmd.Parameters.AddWithValue("@PlanID", PlanID is > 0 ? PlanID.Value : DBNull.Value); diff --git a/DBADash/SQL/SQLQueryStoreTopQueries.sql b/DBADash/SQL/SQLQueryStoreTopQueries.sql index 6320eba3..0b3361a0 100644 --- a/DBADash/SQL/SQLQueryStoreTopQueries.sql +++ b/DBADash/SQL/SQLQueryStoreTopQueries.sql @@ -25,7 +25,7 @@ BEGIN RAISERROR('Invalid database',11,1) RETURN END -IF @GroupBy NOT IN('query_id','plan_id','query_plan_hash','query_hash') +IF @GroupBy NOT IN('query_id','plan_id','query_plan_hash','query_hash','object_id') BEGIN RAISERROR('Invalid group by',11,1) RETURN @@ -70,13 +70,15 @@ SELECT TOP (@Top) DB_NAME() AS DB, ',CASE WHEN @GroupBy = 'plan_id' THEN 'RS.plan_id,P.query_plan_hash,' ELSE '' END,' ',CASE WHEN @GroupBy IN('query_id', 'plan_id') THEN ' - P.query_id query_id, - Q.object_id object_id, + P.query_id, + Q.object_id, Q.query_hash, ISNULL(OBJECT_NAME(Q.object_id),'''') object_name, QT.query_sql_text query_sql_text,' WHEN @GroupBy = 'query_hash' THEN 'Q.query_hash,' WHEN @GroupBy = 'query_plan_hash' THEN 'P.query_plan_hash,' + WHEN @GroupBy = 'object_id' THEN 'Q.object_id, + ISNULL(OBJECT_NAME(Q.object_id),'''') object_name,' ELSE NULL END, CASE WHEN @IncludeWaits = 1 THEN 'STUFF( (SELECT TOP(3) '', '' + CONCAT(W.wait_category_desc, '' = '',SUM(W.total_query_wait_time_ms),''ms'') @@ -113,8 +115,10 @@ SELECT TOP (@Top) ', CASE WHEN COLUMNPROPERTY(OBJECT_ID('sys.query_store_runtime_stats'),'avg_tempdb_space_used','ColumnId') IS NULL THEN '' ELSE 'SUM(RS.avg_tempdb_space_used*RS.count_executions)/NULLIF(SUM(RS.count_executions),0)*8 AS avg_tempdb_space_used_kb,' END,' ', CASE WHEN COLUMNPROPERTY(OBJECT_ID('sys.query_store_runtime_stats'),'max_tempdb_space_used','ColumnId') IS NULL THEN '' ELSE 'MAX(RS.max_tempdb_space_used)*8 AS max_tempdb_space_used_kb,' END + ' MAX(RS.max_dop) AS max_dop, - ', CASE WHEN @GroupBy IN('query_id''plan_id') THEN 'Q.query_parameterization_type_desc,' ELSE '' END, ' - ', CASE WHEN @GroupBy = 'plan_id' THEN 'P.plan_forcing_type_desc,P.force_failure_count,P.last_force_failure_reason_desc,P.is_parallel_plan' ELSE 'COUNT(DISTINCT P.plan_id) num_plans' END, ' + ', CASE WHEN @GroupBy IN('query_id','plan_id') THEN 'Q.query_parameterization_type_desc,' ELSE 'COUNT(DISTINCT Q.query_id) AS num_queries,' END, ' + ', CASE WHEN @GroupBy = 'plan_id' THEN 'P.plan_forcing_type_desc,P.force_failure_count,P.last_force_failure_reason_desc,P.is_parallel_plan,' ELSE 'COUNT(DISTINCT P.plan_id) num_plans,' END, ' + MIN(MIN(RS.first_execution_time)) OVER() interval_start, + MAX(MAX(RS.last_execution_time)) OVER() interval_end FROM sys.query_store_runtime_stats AS RS JOIN sys.query_store_plan AS P ON P.plan_id = RS.plan_id JOIN sys.query_store_query AS Q ON Q.query_id = P.query_id @@ -129,9 +133,11 @@ AND RS.runtime_stats_interval_id <= @interval_to ', CASE WHEN @QueryHash IS NOT NULL THEN 'AND Q.query_hash = @QueryHash' ELSE '' END,' ', CASE WHEN @QueryPlanHash IS NOT NULL THEN 'AND P.query_plan_hash = @QueryPlanHash' ELSE '' END,' ', CASE WHEN @ParallelPlans = 1 THEN 'AND P.is_parallel_plan = 1' ELSE '' END,' +', CASE WHEN @GroupBy = 'object_id' THEN 'AND Q.object_id <> 0' ELSE '' END, ' GROUP BY ',CASE WHEN @GroupBy = 'query_id' THEN 'P.query_id, QT.query_sql_text, Q.object_id,Q.query_hash,Q.query_parameterization_type_desc' WHEN @GroupBy = 'query_plan_hash' THEN 'P.query_plan_hash' WHEN @GroupBy = 'query_hash' THEN 'Q.query_hash' + WHEN @GroupBy = 'object_id' THEN 'Q.object_id' WHEN @GroupBy = 'plan_id' THEN 'P.query_id, QT.query_sql_text, Q.object_id,Q.query_hash,Q.query_parameterization_type_desc,RS.plan_id,P.query_plan_hash,P.plan_forcing_type_desc,P.force_failure_count,P.last_force_failure_reason_desc,P.is_parallel_plan' ELSE NULL END, ' ', CASE WHEN @MinimumPlanCount >1 THEN 'HAVING COUNT(DISTINCT RS.plan_id)>=@MinimumPlanCount' ELSE '' END,' diff --git a/DBADashGUI/DateHelper.cs b/DBADashGUI/DateHelper.cs index dfea8c7e..b7c5588d 100644 --- a/DBADashGUI/DateHelper.cs +++ b/DBADashGUI/DateHelper.cs @@ -69,7 +69,7 @@ public static DataTable ConvertUTCToAppTimeZone(ref DataTable dt, List c { foreach (DataColumn col in dt.Columns) { - if (col.DataType == typeof(DateTime)) + if (col.DataType == typeof(DateTime) || col.DataType == typeof(DateTimeOffset)) { convertColsIdx.Add(col.Ordinal); } @@ -86,11 +86,19 @@ public static DataTable ConvertUTCToAppTimeZone(ref DataTable dt, List c { foreach (DataRow row in dt.Rows) { - foreach (var col in convertColsIdx) + foreach (var colIdx in convertColsIdx) { - if (row[col] != DBNull.Value) + if (row[colIdx] != DBNull.Value) { - row[col] = ((DateTime)row[col]).ToAppTimeZone(); + var colType = dt.Columns[colIdx].DataType; + if (colType == typeof(DateTime)) + { + row[colIdx] = ((DateTime)row[colIdx]).ToAppTimeZone(); + } + else if (colType == typeof(DateTimeOffset)) + { + row[colIdx] = ((DateTimeOffset)row[colIdx]).ToAppTimeZone(); + } } } } @@ -143,6 +151,11 @@ public static DateTime AppTimeZoneToUtc(this DateTime value) return TimeZoneInfo.ConvertTimeToUtc(value, AppTimeZone); } + public static DateTimeOffset ToAppTimeZone(this DateTimeOffset value) + { + return TimeZoneInfo.ConvertTime(value, AppTimeZone); + } + /// /// Returns current DateTime in time zone of app /// diff --git a/DBADashGUI/Performance/QueryStoreTopQueries.Designer.cs b/DBADashGUI/Performance/QueryStoreTopQueries.Designer.cs index 19164a5f..7b956cb4 100644 --- a/DBADashGUI/Performance/QueryStoreTopQueries.Designer.cs +++ b/DBADashGUI/Performance/QueryStoreTopQueries.Designer.cs @@ -37,6 +37,7 @@ private void InitializeComponent() tsNearestInterval = new System.Windows.Forms.ToolStripMenuItem(); includeWaitsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); tsReset = new System.Windows.Forms.ToolStripButton(); + tsColumns = new System.Windows.Forms.ToolStripButton(); tsExecute = new System.Windows.Forms.ToolStripButton(); tsTop = new System.Windows.Forms.ToolStripDropDownButton(); tsTop10 = new System.Windows.Forms.ToolStripMenuItem(); @@ -50,6 +51,7 @@ private void InitializeComponent() planToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); planHashToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); queryHashToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + objectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); tsSort = new System.Windows.Forms.ToolStripDropDownButton(); totalCPUToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); avgCPUToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -100,7 +102,7 @@ private void InitializeComponent() // toolStrip1 // toolStrip1.ImageScalingSize = new System.Drawing.Size(20, 20); - toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { TsCopy, tsExcel, tsOptions, tsReset, tsExecute, tsTop, tsGroupBy, tsSort, tsFilter, toolStripLabel1, txtObjectName, toolStripLabel2, txtPlan }); + toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { TsCopy, tsExcel, tsOptions, tsReset, tsColumns, tsExecute, tsTop, tsGroupBy, tsSort, tsFilter, toolStripLabel1, txtObjectName, toolStripLabel2, txtPlan }); toolStrip1.Location = new System.Drawing.Point(0, 0); toolStrip1.Name = "toolStrip1"; toolStrip1.Size = new System.Drawing.Size(1408, 27); @@ -143,7 +145,7 @@ private void InitializeComponent() tsNearestInterval.CheckOnClick = true; tsNearestInterval.CheckState = System.Windows.Forms.CheckState.Checked; tsNearestInterval.Name = "tsNearestInterval"; - tsNearestInterval.Size = new System.Drawing.Size(221, 26); + tsNearestInterval.Size = new System.Drawing.Size(224, 26); tsNearestInterval.Text = "Use nearest interval"; tsNearestInterval.ToolTipText = "Use the nearest query store interval. Uncheck to filter on first/last execution time"; // @@ -153,8 +155,9 @@ private void InitializeComponent() includeWaitsToolStripMenuItem.CheckOnClick = true; includeWaitsToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; includeWaitsToolStripMenuItem.Name = "includeWaitsToolStripMenuItem"; - includeWaitsToolStripMenuItem.Size = new System.Drawing.Size(221, 26); + includeWaitsToolStripMenuItem.Size = new System.Drawing.Size(224, 26); includeWaitsToolStripMenuItem.Text = "Include Waits"; + includeWaitsToolStripMenuItem.Click += IncludeWaitsToolStripMenuItem_Click; // // tsReset // @@ -166,6 +169,16 @@ private void InitializeComponent() tsReset.Text = "Reset"; tsReset.Click += TsReset_Click; // + // tsColumns + // + tsColumns.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + tsColumns.Image = Properties.Resources.Column_16x; + tsColumns.ImageTransparentColor = System.Drawing.Color.Magenta; + tsColumns.Name = "tsColumns"; + tsColumns.Size = new System.Drawing.Size(29, 24); + tsColumns.Text = "Columns"; + tsColumns.Click += TsColumns_Click; + // // tsExecute // tsExecute.Image = Properties.Resources.ProjectSystemModelRefresh_16x; @@ -233,7 +246,7 @@ private void InitializeComponent() // // tsGroupBy // - tsGroupBy.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { queryToolStripMenuItem, planToolStripMenuItem, planHashToolStripMenuItem, queryHashToolStripMenuItem }); + tsGroupBy.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { queryToolStripMenuItem, planToolStripMenuItem, planHashToolStripMenuItem, queryHashToolStripMenuItem, objectToolStripMenuItem }); tsGroupBy.Image = Properties.Resources.GroupBy_16x; tsGroupBy.ImageTransparentColor = System.Drawing.Color.Magenta; tsGroupBy.Name = "tsGroupBy"; @@ -245,7 +258,7 @@ private void InitializeComponent() queryToolStripMenuItem.Checked = true; queryToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; queryToolStripMenuItem.Name = "queryToolStripMenuItem"; - queryToolStripMenuItem.Size = new System.Drawing.Size(168, 26); + queryToolStripMenuItem.Size = new System.Drawing.Size(224, 26); queryToolStripMenuItem.Tag = "query_id"; queryToolStripMenuItem.Text = "Query"; queryToolStripMenuItem.Click += Select_GroupBy; @@ -253,7 +266,7 @@ private void InitializeComponent() // planToolStripMenuItem // planToolStripMenuItem.Name = "planToolStripMenuItem"; - planToolStripMenuItem.Size = new System.Drawing.Size(168, 26); + planToolStripMenuItem.Size = new System.Drawing.Size(224, 26); planToolStripMenuItem.Tag = "plan_id"; planToolStripMenuItem.Text = "Plan"; planToolStripMenuItem.Click += Select_GroupBy; @@ -261,7 +274,7 @@ private void InitializeComponent() // planHashToolStripMenuItem // planHashToolStripMenuItem.Name = "planHashToolStripMenuItem"; - planHashToolStripMenuItem.Size = new System.Drawing.Size(168, 26); + planHashToolStripMenuItem.Size = new System.Drawing.Size(224, 26); planHashToolStripMenuItem.Tag = "query_plan_hash"; planHashToolStripMenuItem.Text = "Plan Hash"; planHashToolStripMenuItem.Click += Select_GroupBy; @@ -269,11 +282,19 @@ private void InitializeComponent() // queryHashToolStripMenuItem // queryHashToolStripMenuItem.Name = "queryHashToolStripMenuItem"; - queryHashToolStripMenuItem.Size = new System.Drawing.Size(168, 26); + queryHashToolStripMenuItem.Size = new System.Drawing.Size(224, 26); queryHashToolStripMenuItem.Tag = "query_hash"; queryHashToolStripMenuItem.Text = "Query Hash"; queryHashToolStripMenuItem.Click += Select_GroupBy; // + // objectToolStripMenuItem + // + objectToolStripMenuItem.Name = "objectToolStripMenuItem"; + objectToolStripMenuItem.Size = new System.Drawing.Size(224, 26); + objectToolStripMenuItem.Tag = "object_id"; + objectToolStripMenuItem.Text = "Object"; + objectToolStripMenuItem.Click += Select_GroupBy; + // // tsSort // tsSort.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; @@ -511,5 +532,7 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripDropDownButton tsFilter; private System.Windows.Forms.ToolStripMenuItem parallelPlansOnlyToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem minimumPlanCountToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem objectToolStripMenuItem; + private System.Windows.Forms.ToolStripButton tsColumns; } } diff --git a/DBADashGUI/Performance/QueryStoreTopQueries.cs b/DBADashGUI/Performance/QueryStoreTopQueries.cs index 9e5b0ea7..2dfc0b95 100644 --- a/DBADashGUI/Performance/QueryStoreTopQueries.cs +++ b/DBADashGUI/Performance/QueryStoreTopQueries.cs @@ -2,18 +2,12 @@ using DBADashGUI.CustomReports; using DBADashGUI.SchemaCompare; using DBADashGUI.Theme; -using Microsoft.Data.SqlClient; using System; using System.Collections.Generic; using System.Data; using System.Linq; -using System.Text; using System.Threading.Tasks; using System.Windows.Forms; -using Microsoft.SqlServer.TransactSql.ScriptDom; -using Amazon.Runtime.Internal.Transform; -using System.Xml.Linq; -using Serilog; namespace DBADashGUI.Performance { @@ -48,7 +42,7 @@ public void SetContext(DBADashContext context) private int top = 25; private int minimumPlanCount = 1; - private int GetMinimumPlanCount()=>minimumPlanCountToolStripMenuItem.Enabled ? minimumPlanCount : 1; + private int GetMinimumPlanCount() => minimumPlanCountToolStripMenuItem.Enabled ? minimumPlanCount : 1; private string sortColumn = "total_cpu_time_ms"; private QueryStoreTopQueriesMessage.QueryStoreGroupByEnum groupBy = QueryStoreTopQueriesMessage.QueryStoreGroupByEnum.query_id; @@ -167,6 +161,7 @@ private async Task SendMessageAndProcessReply(MessageBase message, DataGridView } var dt = ds.Tables[0]; + DateHelper.ConvertUTCToAppTimeZone(ref dt); if (message is PlanCollectionMessage) { LoadQueryPlan(dt); @@ -177,7 +172,14 @@ private async Task SendMessageAndProcessReply(MessageBase message, DataGridView _dgv.AddColumns(dt, topQueriesResult); _dgv.DataSource = new DataView(dt, null, $"{sortColumn} DESC", DataViewRowState.CurrentRows); - _dgv.LoadColumnLayout(topQueriesResult.ColumnLayout); + try + { + _dgv.LoadColumnLayout(UserColumnLayout ?? topQueriesResult.ColumnLayout); + } + catch (Exception ex) + { + lblStatus.InvokeSetStatus("Error loading column layout", ex.Message, DashColors.Fail); + } _dgv.ApplyTheme(); if (_dgv == dgvDrillDown) { @@ -239,11 +241,14 @@ private void LoadQueryPlan(DataTable dt) { "avg_tempdb_space_used_kb","Avg TempDB KB"}, { "max_tempdb_space_used_kb","Max TempDB KB"}, { "num_plans", "Plan Count" }, + { "num_queries", "Query Count" }, { "top_waits", "Top Waits" }, { "plan_forcing_type_desc","Plan Forcing"}, { "force_failure_count","Force Failure count"}, { "last_force_failure_reason_desc", "Last Forced Failure"}, - {"is_parallel_plan","Parallel"} + {"is_parallel_plan","Parallel"}, + {"interval_start","Interval Start"}, + {"interval_end","Interval End"} }, CellFormatString = new Dictionary { @@ -309,6 +314,12 @@ private void LoadQueryPlan(DataTable dt) new DrillDownLinkColumnInfo() { } + }, + { + "object_name", + new DrillDownLinkColumnInfo() + { + } } }, ColumnLayout = new List>() @@ -341,12 +352,15 @@ private void LoadQueryPlan(DataTable dt) new("avg_tempdb_space_used_kb", new PersistedColumnLayout() { Width = 80, Visible = true }), new("max_tempdb_space_used_kb", new PersistedColumnLayout() { Width = 80, Visible = true }), new("num_plans", new PersistedColumnLayout() { Width = 60, Visible = true }), + new("num_queries", new PersistedColumnLayout() { Width = 60, Visible = true }), new("top_waits", new PersistedColumnLayout() { Width = 200, Visible = true }), - new ("plan_forcing_type_desc", new PersistedColumnLayout() { Width = 100, Visible = true }), - new ("force_failure_count", new PersistedColumnLayout() { Width = 70, Visible = true }), - new ("last_force_failure_reason_desc", new PersistedColumnLayout() { Width = 100, Visible = true }), - new ("is_parallel_plan", new PersistedColumnLayout() { Width = 70, Visible = true }), + new("plan_forcing_type_desc", new PersistedColumnLayout() { Width = 100, Visible = true }), + new("force_failure_count", new PersistedColumnLayout() { Width = 70, Visible = true }), + new("last_force_failure_reason_desc", new PersistedColumnLayout() { Width = 100, Visible = true }), + new("is_parallel_plan", new PersistedColumnLayout() { Width = 70, Visible = true }), new("query_parameterization_type_desc", new PersistedColumnLayout() { Width = 100, Visible = true}), + new("interval_start", new PersistedColumnLayout() { Width = 150, Visible = false}), + new("interval_end", new PersistedColumnLayout() { Width = 150, Visible = false}) }, CellHighlightingRules = { @@ -460,6 +474,14 @@ private void QueryHashDrillDown(DataGridView _dgv, DataGridViewCellEventArgs e) RefreshData(); } + private void ObjectDrillDown(DataGridView _dgv, DataGridViewCellEventArgs e) + { + var objectName = Convert.ToString(_dgv.Rows[e.RowIndex].Cells["object_name"].Value); + txtObjectName.Text = objectName ?? string.Empty; + SetGroupBy(QueryStoreTopQueriesMessage.QueryStoreGroupByEnum.query_id); + RefreshData(); + } + private void PlanHashDrillDown(DataGridView _dgv, DataGridViewCellEventArgs e) { var hash = (string)_dgv.Rows[e.RowIndex].Cells["query_plan_hash"].FormattedValue; @@ -510,6 +532,10 @@ private void Dgv_CellContentClick(object sender, DataGridViewCellEventArgs e) PlanForcingDrillDown(_dgv, e); break; + case "object_name": + ObjectDrillDown(_dgv, e); + break; + default: DefaultDrillDown(_dgv, e); break; @@ -604,6 +630,7 @@ private void SetGroupBy(QueryStoreTopQueriesMessage.QueryStoreGroupByEnum groupB menuItem.Checked = true; tsGroupBy.Text = $@"Group by {menuItem.Text}"; minimumPlanCountToolStripMenuItem.Enabled = groupBy == QueryStoreTopQueriesMessage.QueryStoreGroupByEnum.query_id; + UserColumnLayout = null; } private void Dgv_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) @@ -629,6 +656,7 @@ private void Reset() tsNearestInterval.Checked = true; parallelPlansOnlyToolStripMenuItem.Checked = false; SetMinimumPlanCount(1); + UserColumnLayout = null; } private void ParallelPlans_CheckChanged(object sender, EventArgs e) @@ -651,7 +679,7 @@ private bool IsFilterCustomized() private void MinimumPlanCountToolStripMenuItem_Click(object sender, EventArgs e) { var countString = minimumPlanCount.ToString(); - if (CommonShared.ShowInputDialog(ref countString,"Enter minimum plan count:")== DialogResult.OK) + if (CommonShared.ShowInputDialog(ref countString, "Enter minimum plan count:") == DialogResult.OK) { if (!int.TryParse(countString, out var count)) return; SetMinimumPlanCount(count); @@ -664,5 +692,18 @@ private void SetMinimumPlanCount(int count) minimumPlanCount = count; SetFilterBold(); } + + private List> UserColumnLayout = null; + + private void TsColumns_Click(object sender, EventArgs e) + { + dgv.PromptColumnSelection(); + UserColumnLayout = dgv.GetColumnLayout(); + } + + private void IncludeWaitsToolStripMenuItem_Click(object sender, EventArgs e) + { + UserColumnLayout = null; + } } } \ No newline at end of file