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>
299 lines
16 KiB
Transact-SQL
299 lines
16 KiB
Transact-SQL
-- ===================================================================================================================
|
||
-- SQLI-DB-015: tempdb har for få eller ulige store datafiler
|
||
-- ===================================================================================================================
|
||
-- Regel-id: SQLI-DB-015
|
||
-- Titel: tempdb har for få eller ulige store datafiler
|
||
-- Alvor: Medium for for få filer; Low for ulige store filer.
|
||
-- Versioner: Alle. Ikke relevant på Azure SQL Managed Instance.
|
||
--
|
||
-- Hvad tjekkes, og hvorfor:
|
||
-- Om tempdb har for få eller ulige store datafiler, hvilket giver PAGELATCH-contention på allokeringssider.
|
||
--
|
||
-- Vurdering (sker i C# ud fra de indsamlede data, ikke i SQL'en, medmindre andet er nævnt):
|
||
-- tempdb's datafiler (type ROWS fra Files2005/Files2000, database_id 2) og cpu_count (ServerCollector). Anbefalet =
|
||
-- min(8, logiske CPU'er). Fund 1: antal filer < anbefalet og < 4. Fund 2: mere end én fil, og størrelser eller
|
||
-- autogrowth (MB/%) er ikke ens.
|
||
--
|
||
-- Grænseværdier (konfigurationsnøgle i agentens appsettings.json og standardværdi):
|
||
-- Op til 8 filer, én pr. logisk CPU; under 4 filer (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+
|
||
-- GRANT VIEW ANY DEFINITION TO [agent]; -- metadata; giver IKKE adgang til data
|
||
-- GRANT CONNECT ANY DATABASE TO [agent]; -- SQL Server 2014+; giver IKKE adgang til data
|
||
-- Før 2014 i stedet, i hver brugerdatabase: CREATE USER [agent] FOR LOGIN [agent];
|
||
-- USE msdb; GRANT SELECT ON dbo.backupset TO [agent];
|
||
-- SQL Server 2000 har ikke VIEW SERVER STATE; se docs/legacy-versions.md (fx kræver DBCC SQLPERF(WAITSTATS) forhøjede rettigheder).
|
||
--
|
||
-- Data kommer fra (collector, fil : medlemmer, interval):
|
||
-- DatabaseCollector
|
||
-- Fil: src/SQLInsight.Collectors/Databases/DatabaseCollector.cs
|
||
-- Medlemmer: DatabaseCollector.BuildSql2005(instance, capabilities), DatabaseCollector.BuildSql2000(),
|
||
-- DatabaseCollector.Files2005, DatabaseCollector.Files2000, Db.UtcOffsetSql, DatabaseCollector.VlfSql,
|
||
-- DatabaseCollector.BuildQueryStoreSql(database)
|
||
-- Interval: 15 min som standard (SQLInsight:CollectorIntervals:databases)
|
||
-- Delt script: SQL Scripts/_collectors/DatabaseCollector.sql
|
||
-- ServerCollector
|
||
-- Fil: src/SQLInsight.Collectors/Instance/ServerCollector.cs
|
||
-- Medlemmer: ServerCollector.BuildSql(version), Db.StartTimeSql(version), ServerCollector.Sql2000
|
||
-- Interval: 15 min som standard (SQLInsight:CollectorIntervals:server)
|
||
-- Delt script: SQL Scripts/_collectors/ServerCollector.sql
|
||
-- Regelkode: src/SQLInsight.Rules/Databases/DatabaseRules.cs : TempdbFilesRule.Evaluate
|
||
--
|
||
-- Read-only: Ja: kun SELECT samt xp_msver (læser versionsoplysninger), 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
|
||
|
||
-- -------------------------------------------------------------------------------------------------------------------
|
||
-- DatabaseCollector: Databaseindstillinger og -tilstand, backup-historik (msdb), seneste fejlfri CHECKDB, VLF'er,
|
||
-- Query Store-tilstand og filer.
|
||
-- -------------------------------------------------------------------------------------------------------------------
|
||
|
||
-- Variant: SQL Server 2016 SP2 og nyere (med LastGoodCheckDbTime og availability groups)
|
||
-- Kilde: DatabaseCollector.BuildSql2005(2016 SP2+)
|
||
-- Kode: src/SQLInsight.Collectors/Databases/DatabaseCollector.cs : DatabaseCollector.BuildSql2005(instance, capabilities)
|
||
SELECT d.name, d.database_id, d.state_desc, d.recovery_model_desc, d.compatibility_level, d.page_verify_option_desc,
|
||
d.is_auto_close_on, d.is_auto_shrink_on, d.is_read_only, d.is_trustworthy_on, d.create_date,
|
||
CASE WHEN EXISTS (SELECT 1 FROM sys.database_mirroring m WHERE m.database_id = d.database_id AND m.mirroring_role = 2)
|
||
OR EXISTS (SELECT 1 FROM sys.dm_hadr_database_replica_states rs
|
||
JOIN sys.dm_hadr_availability_replica_states ars ON ars.replica_id = rs.replica_id
|
||
WHERE rs.database_id = d.database_id AND rs.is_local = 1 AND ars.role = 2)
|
||
THEN 1 ELSE 0 END,
|
||
CASE WHEN d.replica_id IS NOT NULL THEN 1 ELSE 0 END,
|
||
CASE WHEN b.last_full >= DATEADD(second, -1, d.create_date) THEN b.last_full END,
|
||
CASE WHEN b.last_diff >= DATEADD(second, -1, d.create_date) THEN b.last_diff END,
|
||
CASE WHEN b.last_log >= DATEADD(second, -1, d.create_date) THEN b.last_log END,
|
||
CAST(DATABASEPROPERTYEX(d.name, 'LastGoodCheckDbTime') AS datetime),
|
||
d.is_auto_create_stats_on, d.is_auto_update_stats_on
|
||
FROM sys.databases d
|
||
LEFT JOIN (
|
||
SELECT database_name,
|
||
MAX(CASE WHEN type = 'D' THEN backup_finish_date END) AS last_full,
|
||
MAX(CASE WHEN type = 'I' THEN backup_finish_date END) AS last_diff,
|
||
MAX(CASE WHEN type = 'L' THEN backup_finish_date END) AS last_log,
|
||
MIN(backup_finish_date) AS first_backup
|
||
FROM msdb.dbo.backupset
|
||
GROUP BY database_name
|
||
) b ON b.database_name = d.name
|
||
WHERE d.name <> N'tempdb'
|
||
GO
|
||
|
||
-- Variant: SQL Server 2012 – 2016 SP1
|
||
-- Kilde: DatabaseCollector.BuildSql2005(2012)
|
||
-- Kode: src/SQLInsight.Collectors/Databases/DatabaseCollector.cs : DatabaseCollector.BuildSql2005(instance, capabilities)
|
||
SELECT d.name, d.database_id, d.state_desc, d.recovery_model_desc, d.compatibility_level, d.page_verify_option_desc,
|
||
d.is_auto_close_on, d.is_auto_shrink_on, d.is_read_only, d.is_trustworthy_on, d.create_date,
|
||
CASE WHEN EXISTS (SELECT 1 FROM sys.database_mirroring m WHERE m.database_id = d.database_id AND m.mirroring_role = 2)
|
||
OR EXISTS (SELECT 1 FROM sys.dm_hadr_database_replica_states rs
|
||
JOIN sys.dm_hadr_availability_replica_states ars ON ars.replica_id = rs.replica_id
|
||
WHERE rs.database_id = d.database_id AND rs.is_local = 1 AND ars.role = 2)
|
||
THEN 1 ELSE 0 END,
|
||
CASE WHEN d.replica_id IS NOT NULL THEN 1 ELSE 0 END,
|
||
CASE WHEN b.last_full >= DATEADD(second, -1, d.create_date) THEN b.last_full END,
|
||
CASE WHEN b.last_diff >= DATEADD(second, -1, d.create_date) THEN b.last_diff END,
|
||
CASE WHEN b.last_log >= DATEADD(second, -1, d.create_date) THEN b.last_log END,
|
||
CAST(NULL AS datetime),
|
||
d.is_auto_create_stats_on, d.is_auto_update_stats_on
|
||
FROM sys.databases d
|
||
LEFT JOIN (
|
||
SELECT database_name,
|
||
MAX(CASE WHEN type = 'D' THEN backup_finish_date END) AS last_full,
|
||
MAX(CASE WHEN type = 'I' THEN backup_finish_date END) AS last_diff,
|
||
MAX(CASE WHEN type = 'L' THEN backup_finish_date END) AS last_log,
|
||
MIN(backup_finish_date) AS first_backup
|
||
FROM msdb.dbo.backupset
|
||
GROUP BY database_name
|
||
) b ON b.database_name = d.name
|
||
WHERE d.name <> N'tempdb'
|
||
GO
|
||
|
||
-- Variant: SQL Server 2005 – 2008 R2
|
||
-- Kilde: DatabaseCollector.BuildSql2005(2005)
|
||
-- Kode: src/SQLInsight.Collectors/Databases/DatabaseCollector.cs : DatabaseCollector.BuildSql2005(instance, capabilities)
|
||
SELECT d.name, d.database_id, d.state_desc, d.recovery_model_desc, d.compatibility_level, d.page_verify_option_desc,
|
||
d.is_auto_close_on, d.is_auto_shrink_on, d.is_read_only, d.is_trustworthy_on, d.create_date,
|
||
CASE WHEN EXISTS (SELECT 1 FROM sys.database_mirroring m WHERE m.database_id = d.database_id AND m.mirroring_role = 2)
|
||
|
||
THEN 1 ELSE 0 END,
|
||
0,
|
||
CASE WHEN b.last_full >= DATEADD(second, -1, d.create_date) THEN b.last_full END,
|
||
CASE WHEN b.last_diff >= DATEADD(second, -1, d.create_date) THEN b.last_diff END,
|
||
CASE WHEN b.last_log >= DATEADD(second, -1, d.create_date) THEN b.last_log END,
|
||
CAST(NULL AS datetime),
|
||
d.is_auto_create_stats_on, d.is_auto_update_stats_on
|
||
FROM sys.databases d
|
||
LEFT JOIN (
|
||
SELECT database_name,
|
||
MAX(CASE WHEN type = 'D' THEN backup_finish_date END) AS last_full,
|
||
MAX(CASE WHEN type = 'I' THEN backup_finish_date END) AS last_diff,
|
||
MAX(CASE WHEN type = 'L' THEN backup_finish_date END) AS last_log,
|
||
MIN(backup_finish_date) AS first_backup
|
||
FROM msdb.dbo.backupset
|
||
GROUP BY database_name
|
||
) b ON b.database_name = d.name
|
||
WHERE d.name <> N'tempdb'
|
||
GO
|
||
|
||
-- Variant: SQL Server 2000 (legacy ODBC-sti)
|
||
-- Kilde: DatabaseCollector.BuildSql2000
|
||
-- Kode: src/SQLInsight.Collectors/Databases/DatabaseCollector.cs : DatabaseCollector.BuildSql2000()
|
||
SELECT d.name, d.dbid,
|
||
CAST(DATABASEPROPERTYEX(d.name, 'Status') AS nvarchar(60)),
|
||
CAST(DATABASEPROPERTYEX(d.name, 'Recovery') AS nvarchar(60)),
|
||
d.cmptlevel,
|
||
CASE WHEN DATABASEPROPERTYEX(d.name, 'IsTornPageDetectionEnabled') = 1 THEN N'TORN_PAGE_DETECTION' ELSE N'NONE' END,
|
||
CAST(DATABASEPROPERTYEX(d.name, 'IsAutoClose') AS int),
|
||
CAST(DATABASEPROPERTYEX(d.name, 'IsAutoShrink') AS int),
|
||
CASE WHEN DATABASEPROPERTYEX(d.name, 'Updateability') = 'READ_ONLY' THEN 1 ELSE 0 END,
|
||
0,
|
||
d.crdate,
|
||
0,
|
||
0,
|
||
CASE WHEN b.last_full >= DATEADD(second, -1, d.crdate) THEN b.last_full END,
|
||
CASE WHEN b.last_diff >= DATEADD(second, -1, d.crdate) THEN b.last_diff END,
|
||
CASE WHEN b.last_log >= DATEADD(second, -1, d.crdate) THEN b.last_log END,
|
||
CAST(NULL AS datetime),
|
||
CAST(DATABASEPROPERTYEX(d.name, 'IsAutoCreateStatistics') AS int),
|
||
CAST(DATABASEPROPERTYEX(d.name, 'IsAutoUpdateStatistics') AS int)
|
||
FROM master.dbo.sysdatabases d
|
||
LEFT JOIN (
|
||
SELECT database_name,
|
||
MAX(CASE WHEN type = 'D' THEN backup_finish_date END) AS last_full,
|
||
MAX(CASE WHEN type = 'I' THEN backup_finish_date END) AS last_diff,
|
||
MAX(CASE WHEN type = 'L' THEN backup_finish_date END) AS last_log,
|
||
MIN(backup_finish_date) AS first_backup
|
||
FROM msdb.dbo.backupset
|
||
GROUP BY database_name
|
||
) b ON b.database_name = d.name
|
||
WHERE d.name <> N'tempdb'
|
||
GO
|
||
|
||
-- Variant: SQL Server 2005 og nyere
|
||
-- Kilde: DatabaseCollector.Files2005
|
||
-- Kode: src/SQLInsight.Collectors/Databases/DatabaseCollector.cs : DatabaseCollector.Files2005
|
||
SELECT database_id, name, type_desc, physical_name,
|
||
CAST(size AS bigint) / 128,
|
||
CASE WHEN is_percent_growth = 1 THEN growth ELSE CAST(growth AS bigint) / 128 END,
|
||
is_percent_growth,
|
||
CASE WHEN max_size = -1 THEN -1 ELSE CAST(max_size AS bigint) / 128 END
|
||
FROM sys.master_files
|
||
WHERE type_desc IN (N'ROWS', N'LOG')
|
||
GO
|
||
|
||
-- Variant: SQL Server 2000 (legacy ODBC-sti)
|
||
-- Kilde: DatabaseCollector.Files2000
|
||
-- Kode: src/SQLInsight.Collectors/Databases/DatabaseCollector.cs : DatabaseCollector.Files2000
|
||
SELECT dbid, name, CASE WHEN groupid = 0 THEN N'LOG' ELSE N'ROWS' END, filename,
|
||
CAST(size AS bigint) / 128,
|
||
CASE WHEN status & 0x100000 <> 0 THEN growth ELSE CAST(growth AS bigint) / 128 END,
|
||
CASE WHEN status & 0x100000 <> 0 THEN 1 ELSE 0 END,
|
||
CASE WHEN maxsize = -1 THEN -1 ELSE CAST(maxsize AS bigint) / 128 END
|
||
FROM master.dbo.sysaltfiles
|
||
GO
|
||
|
||
-- Variant: Alle versioner: serverens afstand til UTC (msdb-tider er lokal tid)
|
||
-- Kilde: Db.UtcOffsetSql
|
||
-- Kode: src/SQLInsight.Collectors/Db.cs : Db.UtcOffsetSql
|
||
SELECT DATEDIFF(minute, GETUTCDATE(), GETDATE())
|
||
GO
|
||
|
||
-- Variant: SQL Server 2016 SP2 og nyere (sys.dm_db_log_info)
|
||
-- Kilde: DatabaseCollector.VlfSql
|
||
-- Kode: src/SQLInsight.Collectors/Databases/DatabaseCollector.cs : DatabaseCollector.VlfSql
|
||
SELECT d.database_id, COUNT(*)
|
||
FROM sys.databases d
|
||
CROSS APPLY sys.dm_db_log_info(d.database_id) li
|
||
WHERE d.state = 0
|
||
GROUP BY d.database_id
|
||
GO
|
||
|
||
-- Variant: SQL Server 2016 og nyere: én gang pr. online brugerdatabase (vist for msdb)
|
||
-- Kilde: DatabaseCollector.BuildQueryStoreSql
|
||
-- Kode: src/SQLInsight.Collectors/Databases/DatabaseCollector.cs : DatabaseCollector.BuildQueryStoreSql(database)
|
||
SELECT desired_state_desc, actual_state_desc, readonly_reason, current_storage_size_mb, max_storage_size_mb
|
||
FROM [msdb].sys.database_query_store_options
|
||
GO
|
||
|
||
-- -------------------------------------------------------------------------------------------------------------------
|
||
-- ServerCollector: Starttidspunkt, logiske CPU'er, schedulers, NUMA-noder, fysisk hukommelse og virtualisering.
|
||
-- -------------------------------------------------------------------------------------------------------------------
|
||
|
||
-- Variant: SQL Server 2012 og nyere
|
||
-- Kilde: ServerCollector.BuildSql(2012+)
|
||
-- Kode: src/SQLInsight.Collectors/Instance/ServerCollector.cs : ServerCollector.BuildSql(version)
|
||
SELECT
|
||
(SELECT sqlserver_start_time FROM sys.dm_os_sys_info),
|
||
cpu_count,
|
||
(SELECT COUNT(*) FROM sys.dm_os_schedulers WHERE status = N'VISIBLE ONLINE'),
|
||
(SELECT COUNT(DISTINCT parent_node_id) FROM sys.dm_os_schedulers WHERE status = N'VISIBLE ONLINE'),
|
||
physical_memory_kb / 1024,
|
||
virtual_machine_type_desc
|
||
FROM sys.dm_os_sys_info
|
||
GO
|
||
|
||
-- Variant: SQL Server 2008 R2 SP1 og senere 2008 R2-builds
|
||
-- Kilde: ServerCollector.BuildSql(2008 R2 SP1)
|
||
-- Kode: src/SQLInsight.Collectors/Instance/ServerCollector.cs : ServerCollector.BuildSql(version)
|
||
SELECT
|
||
(SELECT sqlserver_start_time FROM sys.dm_os_sys_info),
|
||
cpu_count,
|
||
(SELECT COUNT(*) FROM sys.dm_os_schedulers WHERE status = N'VISIBLE ONLINE'),
|
||
(SELECT COUNT(DISTINCT parent_node_id) FROM sys.dm_os_schedulers WHERE status = N'VISIBLE ONLINE'),
|
||
physical_memory_in_bytes / 1048576,
|
||
virtual_machine_type_desc
|
||
FROM sys.dm_os_sys_info
|
||
GO
|
||
|
||
-- Variant: SQL Server 2008 og 2008 R2 før SP1
|
||
-- Kilde: ServerCollector.BuildSql(2008)
|
||
-- Kode: src/SQLInsight.Collectors/Instance/ServerCollector.cs : ServerCollector.BuildSql(version)
|
||
SELECT
|
||
(SELECT sqlserver_start_time FROM sys.dm_os_sys_info),
|
||
cpu_count,
|
||
(SELECT COUNT(*) FROM sys.dm_os_schedulers WHERE status = N'VISIBLE ONLINE'),
|
||
(SELECT COUNT(DISTINCT parent_node_id) FROM sys.dm_os_schedulers WHERE status = N'VISIBLE ONLINE'),
|
||
physical_memory_in_bytes / 1048576,
|
||
CAST(NULL AS nvarchar(60))
|
||
FROM sys.dm_os_sys_info
|
||
GO
|
||
|
||
-- Variant: SQL Server 2005
|
||
-- Kilde: ServerCollector.BuildSql(2005)
|
||
-- Kode: src/SQLInsight.Collectors/Instance/ServerCollector.cs : ServerCollector.BuildSql(version)
|
||
SELECT
|
||
(SELECT create_date FROM sys.databases WHERE database_id = 2),
|
||
cpu_count,
|
||
(SELECT COUNT(*) FROM sys.dm_os_schedulers WHERE status = N'VISIBLE ONLINE'),
|
||
(SELECT COUNT(DISTINCT parent_node_id) FROM sys.dm_os_schedulers WHERE status = N'VISIBLE ONLINE'),
|
||
physical_memory_in_bytes / 1048576,
|
||
CAST(NULL AS nvarchar(60))
|
||
FROM sys.dm_os_sys_info
|
||
GO
|
||
|
||
-- Variant: SQL Server 2000 (legacy ODBC-sti): starttidspunkt
|
||
-- Kilde: ServerCollector.StartTimeSql(2000)
|
||
-- Kode: src/SQLInsight.Collectors/Db.cs : Db.StartTimeSql(version)
|
||
SELECT (SELECT login_time FROM master.dbo.sysprocesses WHERE spid = 1)
|
||
GO
|
||
|
||
-- Variant: SQL Server 2000 (legacy ODBC-sti): CPU og hukommelse
|
||
-- Kilde: ServerCollector.Sql2000
|
||
-- Kode: src/SQLInsight.Collectors/Instance/ServerCollector.cs : ServerCollector.Sql2000
|
||
EXEC master.dbo.xp_msver
|
||
GO
|