Skip to content
Merged
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
18 changes: 14 additions & 4 deletions src/PlanViewer.Core/Services/QueryStoreService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,29 @@ public static async Task<List<QueryStorePlan>> FetchTopPlansAsync(
};

// Build optional WHERE clauses from filter (parameterized for safety).
// Filters are applied in Phase 3 (BEFORE TOP N) so the user's target
// query isn't excluded just because it's not in the top N by CPU.
// Aliases here reference Phase 3's CTE: ps (#plan_stats),
// p (sys.query_store_plan), q (sys.query_store_query).
var filterClauses = new List<string>();
var parameters = new List<SqlParameter>();
var needsQueryJoin = false;

if (filter?.QueryId != null)
{
filterClauses.Add("AND q.query_id = @filterQueryId");
filterClauses.Add("AND p.query_id = @filterQueryId");
parameters.Add(new SqlParameter("@filterQueryId", filter.QueryId.Value));
}
if (filter?.PlanId != null)
{
filterClauses.Add("AND tp.plan_id = @filterPlanId");
filterClauses.Add("AND ps.plan_id = @filterPlanId");
parameters.Add(new SqlParameter("@filterPlanId", filter.PlanId.Value));
}
if (!string.IsNullOrWhiteSpace(filter?.QueryHash))
{
filterClauses.Add("AND q.query_hash = CONVERT(binary(8), @filterQueryHash, 1)");
parameters.Add(new SqlParameter("@filterQueryHash", filter.QueryHash.Trim()));
needsQueryJoin = true;
}
if (!string.IsNullOrWhiteSpace(filter?.QueryPlanHash))
{
Expand All @@ -122,11 +128,15 @@ public static async Task<List<QueryStorePlan>> FetchTopPlansAsync(
filterClauses.Add("AND OBJECT_SCHEMA_NAME(q.object_id) + N'.' + OBJECT_NAME(q.object_id) = @filterModule");
}
parameters.Add(new SqlParameter("@filterModule", moduleVal));
needsQueryJoin = true;
}

var rnClause = filter?.PlanId != null ? "" : "AND r.rn = 1";
var filterSql = filterClauses.Count > 0
? "\n" + string.Join("\n", filterClauses)
? "\n " + string.Join("\n ", filterClauses)
: "";
var phase3QueryJoin = needsQueryJoin
? " JOIN sys.query_store_query AS q ON p.query_id = q.query_id\n"
: "";

// Time-range filter: always filter on interval start_time (indexed).
Expand Down Expand Up @@ -241,6 +251,7 @@ CASE WHEN ps.total_executions > 0
ROW_NUMBER() OVER (PARTITION BY p.query_id ORDER BY {orderClause} DESC) AS rn
FROM #plan_stats AS ps
JOIN sys.query_store_plan AS p ON ps.plan_id = p.plan_id
{phase3QueryJoin} WHERE 1 = 1{filterSql}
)
SELECT TOP ({topN})
r.query_id,
Expand Down Expand Up @@ -295,7 +306,6 @@ ELSE N''
JOIN sys.query_store_plan AS p ON tp.plan_id = p.plan_id
JOIN sys.query_store_query AS q ON p.query_id = q.query_id
JOIN sys.query_store_query_text AS qt ON q.query_text_id = qt.query_text_id
WHERE 1 = 1{filterSql}
ORDER BY {outerOrder} DESC;";

var plans = new List<QueryStorePlan>();
Expand Down
Loading