sqlinsight-pro/SQL Scripts/_collectors/IndexCollector.sql
Søren Eggert Lundsteen Olsen 631edc2adc
Some checks failed
deploy community / deploy (push) Successful in 15s
deploy hub / deploy (push) Successful in 19s
CI / Build og unit tests (push) Successful in 7s
CI / MSI-installer (push) Successful in 17s
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Has been cancelled
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Has been cancelled
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Has been cancelled
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Has been cancelled
Review 1.3: SQL Scripts for database discovery and the permission check
Registers 1.3's SQL in SqlScriptSources (DatabaseDiscovery: databases 2005/2000
and the Dynamics AX signature per database; PermissionCheck: login/sysadmin, the
HAS_PERMS_BY_NAME checks for 2005-2012, 2014-2019/MI and 2022+, databases without
access) and adds _collectors/DatabaseDiscovery.sql and PermissionCheck.sql with
Danish review headers. The constants are renamed *Sql and the login query moved to
PermissionCheck.WhoSql so the registry test finds them. The scripts of the
collectors that now skip unchosen databases say so (their SQL is unchanged).

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

125 lines
8.3 KiB
Transact-SQL

-- ===================================================================================================================
-- Collector: IndexCollector ("indexes")
-- ===================================================================================================================
-- Hvad: Index-inventar med brug, manglende index-forslag og statistikker for hver læsbar brugerdatabase. Kun
-- metadata og DMV'er; ingen tabeldata, ingen scanning.
-- Fil: src/SQLInsight.Collectors/Databases/IndexCollector.cs
-- Databaser: Fra 1.3 kun de databaser, der er valgt under Servere og databaser (systemdatabaserne altid; se
-- _collectors/DatabaseDiscovery.sql). Fravalgte databaser sorteres fra i koden efter forespørgslen; SQL'en er uændret.
-- Interval: 6 timer som standard (SQLInsight:CollectorIntervals:indexes)
-- Versioner: SQL Server 2005 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+
-- 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];
--
-- Bruges af: SQLI-IDX-001, SQLI-IDX-002, SQLI-IDX-003, SQLI-IDX-004, SQLI-STAT-001
-- 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 2008 og nyere: starttidspunkt
-- Kilde: IndexCollector.StartTimeSql(2008+)
-- Kode: src/SQLInsight.Collectors/Db.cs : Db.StartTimeSql(version)
SELECT (SELECT sqlserver_start_time FROM sys.dm_os_sys_info)
GO
-- Variant: SQL Server 2005: starttidspunkt
-- Kilde: IndexCollector.StartTimeSql(2005)
-- Kode: src/SQLInsight.Collectors/Db.cs : Db.StartTimeSql(version)
SELECT (SELECT create_date FROM sys.databases WHERE database_id = 2)
GO
-- Variant: SQL Server 2005 og nyere
-- Kilde: IndexCollector.DatabasesSql
-- Kode: src/SQLInsight.Collectors/Databases/IndexCollector.cs : IndexCollector.DatabasesSql
SELECT d.name FROM sys.databases d
WHERE d.state = 0 AND d.database_id > 4 AND HAS_DBACCESS(d.name) = 1
AND NOT EXISTS (SELECT 1 FROM sys.database_mirroring m WHERE m.database_id = d.database_id AND m.mirroring_role = 2)
GO
-- Variant: SQL Server 2005 og nyere
-- Kilde: IndexCollector.MissingSql
-- Kode: src/SQLInsight.Collectors/Databases/IndexCollector.cs : IndexCollector.MissingSql
SELECT DB_NAME(d.database_id), d.statement, d.equality_columns, d.inequality_columns, d.included_columns,
gs.user_seeks, gs.user_scans, gs.avg_total_user_cost, gs.avg_user_impact, gs.last_user_seek
FROM sys.dm_db_missing_index_details d
JOIN sys.dm_db_missing_index_groups g ON g.index_handle = d.index_handle
JOIN sys.dm_db_missing_index_group_stats gs ON gs.group_handle = g.index_group_handle
GO
-- Variant: SQL Server 2008 og nyere: én gang pr. database (vist for msdb)
-- Kilde: IndexCollector.BuildIndexSql(2008+)
-- Kode: src/SQLInsight.Collectors/Databases/IndexCollector.cs : IndexCollector.BuildIndexSql(database, filteredIndexes: true)
SELECT s.name, o.name, i.index_id, i.name, i.type_desc, i.is_unique, i.is_primary_key, i.is_unique_constraint, i.is_disabled,
i.filter_definition,
STUFF((SELECT NCHAR(57344) + c.name + CASE WHEN ic.is_descending_key = 1 THEN N' DESC' ELSE N'' END
FROM [msdb].sys.index_columns ic JOIN [msdb].sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 0
AND ic.key_ordinal > 0
ORDER BY ic.key_ordinal
FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), 1, 1, N''),
STUFF((SELECT NCHAR(57344) + c.name + CASE WHEN ic.is_descending_key = 1 THEN N' DESC' ELSE N'' END
FROM [msdb].sys.index_columns ic JOIN [msdb].sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 1
ORDER BY c.name
FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), 1, 1, N''),
ps.row_count, ps.used_pages,
us.user_seeks, us.user_scans, us.user_lookups, us.user_updates,
fw.forwarded
FROM [msdb].sys.indexes i
JOIN [msdb].sys.objects o ON o.object_id = i.object_id AND o.type = 'U' AND o.is_ms_shipped = 0
JOIN [msdb].sys.schemas s ON s.schema_id = o.schema_id
OUTER APPLY (SELECT SUM(p.row_count) AS row_count, SUM(p.used_page_count) AS used_pages
FROM [msdb].sys.dm_db_partition_stats p WHERE p.object_id = i.object_id AND p.index_id = i.index_id) ps
LEFT JOIN sys.dm_db_index_usage_stats us ON us.database_id = DB_ID(N'msdb') AND us.object_id = i.object_id AND us.index_id = i.index_id
OUTER APPLY (SELECT SUM(os.forwarded_fetch_count) AS forwarded
FROM sys.dm_db_index_operational_stats(DB_ID(N'msdb'), i.object_id, 0, NULL) os WHERE i.index_id = 0) fw;
GO
-- Variant: SQL Server 2005: én gang pr. database (vist for msdb)
-- Kilde: IndexCollector.BuildIndexSql(2005)
-- Kode: src/SQLInsight.Collectors/Databases/IndexCollector.cs : IndexCollector.BuildIndexSql(database, filteredIndexes: false)
SELECT s.name, o.name, i.index_id, i.name, i.type_desc, i.is_unique, i.is_primary_key, i.is_unique_constraint, i.is_disabled,
CAST(NULL AS nvarchar(max)),
STUFF((SELECT NCHAR(57344) + c.name + CASE WHEN ic.is_descending_key = 1 THEN N' DESC' ELSE N'' END
FROM [msdb].sys.index_columns ic JOIN [msdb].sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 0
AND ic.key_ordinal > 0
ORDER BY ic.key_ordinal
FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), 1, 1, N''),
STUFF((SELECT NCHAR(57344) + c.name + CASE WHEN ic.is_descending_key = 1 THEN N' DESC' ELSE N'' END
FROM [msdb].sys.index_columns ic JOIN [msdb].sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 1
ORDER BY c.name
FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), 1, 1, N''),
ps.row_count, ps.used_pages,
us.user_seeks, us.user_scans, us.user_lookups, us.user_updates,
fw.forwarded
FROM [msdb].sys.indexes i
JOIN [msdb].sys.objects o ON o.object_id = i.object_id AND o.type = 'U' AND o.is_ms_shipped = 0
JOIN [msdb].sys.schemas s ON s.schema_id = o.schema_id
OUTER APPLY (SELECT SUM(p.row_count) AS row_count, SUM(p.used_page_count) AS used_pages
FROM [msdb].sys.dm_db_partition_stats p WHERE p.object_id = i.object_id AND p.index_id = i.index_id) ps
LEFT JOIN sys.dm_db_index_usage_stats us ON us.database_id = DB_ID(N'msdb') AND us.object_id = i.object_id AND us.index_id = i.index_id
OUTER APPLY (SELECT SUM(os.forwarded_fetch_count) AS forwarded
FROM sys.dm_db_index_operational_stats(DB_ID(N'msdb'), i.object_id, 0, NULL) os WHERE i.index_id = 0) fw;
GO
-- Variant: SQL Server 2005 og nyere: én gang pr. database (vist for msdb)
-- Kilde: IndexCollector.BuildStatisticsSql
-- Kode: src/SQLInsight.Collectors/Databases/IndexCollector.cs : IndexCollector.BuildStatisticsSql(database)
SELECT s.name, o.name, si.name, STATS_DATE(si.id, si.indid),
COALESCE((SELECT TOP 1 t.rowcnt FROM [msdb].sys.sysindexes t WHERE t.id = si.id AND t.indid IN (0, 1)), si.rowcnt), si.rowmodctr
FROM [msdb].sys.sysindexes si
JOIN [msdb].sys.objects o ON o.object_id = si.id AND o.type = 'U' AND o.is_ms_shipped = 0
JOIN [msdb].sys.schemas s ON s.schema_id = o.schema_id
WHERE si.indid > 0 AND si.indid < 255;
GO