sqlinsight-pro/SQL Scripts/SQLI-PLAN-003.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

126 lines
7.5 KiB
Transact-SQL
Raw Permalink 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.

-- ===================================================================================================================
-- SQLI-PLAN-003: Skalar funktion kaldes for hver række
-- ===================================================================================================================
-- Regel-id: SQLI-PLAN-003
-- Titel: Skalar funktion kaldes for hver række
-- Alvor: Medium
-- Versioner: SQL Server 2005 SP2 og nyere.
--
-- Hvad tjekkes, og hvorfor:
-- Om en tung forespørgsel kalder en skalar brugerfunktion for hver række.
--
-- Vurdering (sker i C# ud fra de indsamlede data, ikke i SQL'en, medmindre andet er nævnt):
-- Planen analyseres i C#: fund hvis den indeholder UserDefinedFunction-noder, og sætningens samlede CPU-tid er
-- mindst 60 sekunder. Højst 10 fund.
--
-- Grænseværdier (konfigurationsnøgle i agentens appsettings.json og standardværdi):
-- 60.000.000 µs CPU (ScalarUdfRule.MinTotalWorkerTimeUs, fast i koden).
--
-- 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+
--
-- Data kommer fra (collector, fil : medlemmer, interval):
-- PlanAnalysisCollector
-- Fil: src/SQLInsight.Collectors/Performance/PlanAnalysisCollector.cs
-- Medlemmer: PlanAnalysisCollector.BuildSql(memoryColumns: true, queryHash: true),
-- PlanAnalysisCollector.BuildSql(memoryColumns: false, queryHash: true),
-- PlanAnalysisCollector.BuildSql(memoryColumns: false, queryHash: false)
-- Interval: 30 min som standard (SQLInsight:CollectorIntervals:plan-analysis)
-- Delt script: SQL Scripts/_collectors/PlanAnalysisCollector.sql
-- Regelkode: src/SQLInsight.Rules/Performance/PlanRules.cs : ScalarUdfRule
--
-- Read-only: Ja: kun SELECT samt SET-sessionsindstillinger. Intet ændrer data, indstillinger eller objekter.
-- Kørsel: Kør i SSMS som agentens login (eller et login med samme rettigheder). Hver variant er sin egen batch
-- (GO); en variant for en anden SQL Server-version end din kan fejle - det er forventet.
-- Per-database-forespørgsler er vist for databasen msdb, som findes overalt: ret navnet til den
-- database, du vil se.
-- ===================================================================================================================
-- -------------------------------------------------------------------------------------------------------------------
-- Forbindelsen: Sessionsindstillinger, agenten sætter på hver forbindelse
-- -------------------------------------------------------------------------------------------------------------------
-- Variant: Alle versioner: sættes på hver forbindelse, agenten åbner
-- Kilde: SqlConnectionFactory.SessionSettings
-- Kode: src/SQLInsight.Connectivity/SqlConnectionFactory.cs : SqlConnectionFactory.SessionSettings
SET LOCK_TIMEOUT 5000; SET DEADLOCK_PRIORITY LOW; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
GO
-- -------------------------------------------------------------------------------------------------------------------
-- PlanAnalysisCollector: De 50 sætninger med mest CPU det seneste døgn og deres cachede planer (som tekst), som
-- analyseres i C# (ShowplanAnalyzer).
-- -------------------------------------------------------------------------------------------------------------------
-- 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