sqlinsight-pro/SQL Scripts/_collectors/PlanAnalysisCollector.sql
Søren Eggert Lundsteen Olsen 2284e7f082 SQL Scripts: one reviewable script per rule
A DBA should be able to review every statement SQLInsight sends to a
monitored server before granting it access. "SQL Scripts/<rule-id>.sql"
holds, per rule (82 built-in, 16 community), a Danish header (what and
why, how C# judges the data, thresholds with config keys, severity,
exact GRANTs, versions, collector file and member) and the collector's
SQL verbatim, every version variant as its own batch.
"_collectors/<Collector>.sql" holds each collector's queries once.

The only statement that is not strictly read-only is documented in the
scripts: the deadlock collector's session temp table (#system_health).

Inline SQL in the collectors moved to internal constants and Build*Sql
methods (no text changed), so SqlScriptTests can check that the scripts
still contain the SQL the code sends, that every rule has a script, that
every collector query is registered, and that nothing writes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 13:20:06 +02:00

91 lines
5.5 KiB
Transact-SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- ===================================================================================================================
-- Collector: PlanAnalysisCollector ("plan-analysis")
-- ===================================================================================================================
-- Hvad: De 50 sætninger med mest CPU det seneste døgn og deres cachede planer (som tekst), som analyseres i C#
-- (ShowplanAnalyzer).
-- Fil: src/SQLInsight.Collectors/Performance/PlanAnalysisCollector.cs
-- Interval: 30 min som standard (SQLInsight:CollectorIntervals:plan-analysis)
-- Versioner: SQL Server 2005 SP2 og nyere
--
-- Rettigheder til agentens login (se db/permissions/grant-least-privilege.sql; [agent] = agentens login):
-- GRANT VIEW SERVER STATE TO [agent]; -- SQL Server 2005-2019
-- GRANT VIEW SERVER PERFORMANCE STATE TO [agent]; -- SQL Server 2022+ (erstatter VIEW SERVER STATE)
-- GRANT VIEW SERVER SECURITY STATE TO [agent]; -- SQL Server 2022+
--
-- Bruges af: SQLI-PLAN-001, SQLI-PLAN-002, SQLI-PLAN-003, SQLI-PLAN-004, SQLI-PLAN-005
-- Read-only: Ja: kun SELECT samt SET-sessionsindstillinger. Intet ændrer data, indstillinger eller objekter.
-- Kørsel: Kør i SSMS som agentens login. Hver variant er sin egen batch (GO); en variant for en anden SQL Server-version
-- end din kan fejle - det er forventet. Agenten kører altid med sessionsindstillingerne i _collectors/InstanceProbe.sql.
-- ===================================================================================================================
-- Variant: SQL Server 2016 SP2 / 2017 CU3 og nyere (med spill- og memory grant-kolonner)
-- Kilde: PlanAnalysisCollector.BuildSql(2016 SP2+)
-- Kode: src/SQLInsight.Collectors/Performance/PlanAnalysisCollector.cs : PlanAnalysisCollector.BuildSql(memoryColumns: true, queryHash: true)
WITH top_statements AS (
SELECT TOP (50) qs.*
FROM sys.dm_exec_query_stats qs
WHERE qs.last_execution_time >= DATEADD(hour, -24, GETDATE())
ORDER BY qs.total_worker_time DESC
)
SELECT CONVERT(varchar(20), qs.query_hash, 1),
DB_NAME(CONVERT(int, pa.value)),
LEFT(SUBSTRING(t.text, qs.statement_start_offset / 2 + 1,
(CASE WHEN qs.statement_end_offset = -1 THEN DATALENGTH(t.text) ELSE qs.statement_end_offset END - qs.statement_start_offset) / 2 + 1), 4000),
qs.execution_count, qs.total_worker_time, qs.total_elapsed_time, qs.total_logical_reads,
qs.total_spills, qs.total_grant_kb, qs.total_used_grant_kb,
qs.last_execution_time,
tp.query_plan
FROM top_statements qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t
OUTER APPLY sys.dm_exec_text_query_plan(qs.plan_handle, qs.statement_start_offset, qs.statement_end_offset) tp
OUTER APPLY (SELECT value FROM sys.dm_exec_plan_attributes(qs.plan_handle) WHERE attribute = N'dbid') pa
ORDER BY qs.total_worker_time DESC;
GO
-- Variant: SQL Server 2008 – 2016 SP1 og 2017 før CU3
-- Kilde: PlanAnalysisCollector.BuildSql(2008)
-- Kode: src/SQLInsight.Collectors/Performance/PlanAnalysisCollector.cs : PlanAnalysisCollector.BuildSql(memoryColumns: false, queryHash: true)
WITH top_statements AS (
SELECT TOP (50) qs.*
FROM sys.dm_exec_query_stats qs
WHERE qs.last_execution_time >= DATEADD(hour, -24, GETDATE())
ORDER BY qs.total_worker_time DESC
)
SELECT CONVERT(varchar(20), qs.query_hash, 1),
DB_NAME(CONVERT(int, pa.value)),
LEFT(SUBSTRING(t.text, qs.statement_start_offset / 2 + 1,
(CASE WHEN qs.statement_end_offset = -1 THEN DATALENGTH(t.text) ELSE qs.statement_end_offset END - qs.statement_start_offset) / 2 + 1), 4000),
qs.execution_count, qs.total_worker_time, qs.total_elapsed_time, qs.total_logical_reads,
CAST(NULL AS bigint), CAST(NULL AS bigint), CAST(NULL AS bigint),
qs.last_execution_time,
tp.query_plan
FROM top_statements qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t
OUTER APPLY sys.dm_exec_text_query_plan(qs.plan_handle, qs.statement_start_offset, qs.statement_end_offset) tp
OUTER APPLY (SELECT value FROM sys.dm_exec_plan_attributes(qs.plan_handle) WHERE attribute = N'dbid') pa
ORDER BY qs.total_worker_time DESC;
GO
-- Variant: SQL Server 2005 SP2 og nyere 2005-builds
-- Kilde: PlanAnalysisCollector.BuildSql(2005)
-- Kode: src/SQLInsight.Collectors/Performance/PlanAnalysisCollector.cs : PlanAnalysisCollector.BuildSql(memoryColumns: false, queryHash: false)
WITH top_statements AS (
SELECT TOP (50) qs.*
FROM sys.dm_exec_query_stats qs
WHERE qs.last_execution_time >= DATEADD(hour, -24, GETDATE())
ORDER BY qs.total_worker_time DESC
)
SELECT sys.fn_varbintohexstr(qs.sql_handle) + ':' + CAST(qs.statement_start_offset AS varchar(12)) + ':' + CAST(qs.statement_end_offset AS varchar(12)),
DB_NAME(CONVERT(int, pa.value)),
LEFT(SUBSTRING(t.text, qs.statement_start_offset / 2 + 1,
(CASE WHEN qs.statement_end_offset = -1 THEN DATALENGTH(t.text) ELSE qs.statement_end_offset END - qs.statement_start_offset) / 2 + 1), 4000),
qs.execution_count, qs.total_worker_time, qs.total_elapsed_time, qs.total_logical_reads,
CAST(NULL AS bigint), CAST(NULL AS bigint), CAST(NULL AS bigint),
qs.last_execution_time,
tp.query_plan
FROM top_statements qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t
OUTER APPLY sys.dm_exec_text_query_plan(qs.plan_handle, qs.statement_start_offset, qs.statement_end_offset) tp
OUTER APPLY (SELECT value FROM sys.dm_exec_plan_attributes(qs.plan_handle) WHERE attribute = N'dbid') pa
ORDER BY qs.total_worker_time DESC;
GO