ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

Как извСстно индСксы ΠΈΠ³Ρ€Π°ΡŽΡ‚ Π²Π°ΠΆΠ½ΡƒΡŽ Ρ€ΠΎΠ»ΡŒ Π² Π‘Π£Π‘Π”, прСдоставляя быстрый поиск ΠΊ Π½ΡƒΠΆΠ½Ρ‹ΠΌ записям. ΠŸΠΎΡ‚ΠΎΠΌΡƒ Ρ‚Π°ΠΊ Π²Π°ΠΆΠ½ΠΎ ΠΈΡ… своСврСмСнно ΠΎΠ±ΡΠ»ΡƒΠΆΠΈΠ²Π°Ρ‚ΡŒ. Об Π°Π½Π°Π»ΠΈΠ·Π΅ ΠΈ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ написано достаточно ΠΌΠ½ΠΎΠ³ΠΎ ΠΌΠ°Ρ‚Π΅Ρ€ΠΈΠ°Π»Π°, Π² Ρ‚ΠΎΠΌ числС ΠΈ Π² Π˜Π½Ρ‚Π΅Ρ€Π½Π΅Ρ‚Π΅. НапримСр, Π½Π΅Π΄Π°Π²Π½ΠΎ дСлался ΠΎΠ±Π·ΠΎΡ€ Π΄Π°Π½Π½ΠΎΠΉ Ρ‚Π΅ΠΌΡ‹ Π² этой ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ.

БущСствуСт мноТСство ΠΊΠ°ΠΊ ΠΏΠ»Π°Ρ‚Π½Ρ‹Ρ…, Ρ‚Π°ΠΊ ΠΈ бСсплатных Ρ€Π΅ΡˆΠ΅Π½ΠΈΠΉ для этого. НапримСр, Π΅ΡΡ‚ΡŒ Π³ΠΎΡ‚ΠΎΠ²ΠΎΠ΅ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅, основанноС Π½Π° Π°Π΄Π°ΠΏΡ‚ΠΈΠ²Π½ΠΎΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄Π΅ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ индСксов.

Π”Π°Π»Π΅Π΅ рассмотрим Π±Π΅ΡΠΏΠ»Π°Ρ‚Π½ΡƒΡŽ ΡƒΡ‚ΠΈΠ»ΠΈΡ‚Ρƒ SQLIndexManager, Π°Π²Ρ‚ΠΎΡ€ΠΎΠΌ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ являСтся AlanDenton.

ОсновноС тСхничСскоС Ρ€Π°Π·Π»ΠΈΡ‡ΠΈΠ΅ ΠΌΠ΅ΠΆΠ΄Ρƒ SQLIndexManager ΠΈ ряда Π΄Ρ€ΡƒΠ³ΠΈΡ… Π°Π½Π°Π»ΠΎΠ³ΠΎΠ² ΠΏΡ€ΠΈΠ²ΠΎΠ΄ΠΈΡ‚ сам Π°Π²Ρ‚ΠΎΡ€ здСсь ΠΈ здСсь.

Π’ этой ΠΆΠ΅ ΡΡ‚Π°Ρ‚ΡŒΠ΅ со стороны взглянСм Π½Π° ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ ΠΈ Π½Π° возмоТности эксплуатации Π΄Π°Π½Π½ΠΎΠ³ΠΎ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ½ΠΎΠ³ΠΎ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ.

ΠžΠ±ΡΡƒΠΆΠ΄Π°ΡŽΡ‚ Π΄Π°Π½Π½ΡƒΡŽ ΡƒΡ‚ΠΈΠ»ΠΈΡ‚Ρƒ здСсь.
Π‘ΠΎ Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ большая Ρ‡Π°ΡΡ‚ΡŒ Π·Π°ΠΌΠ΅Ρ‡Π°Π½ΠΈΠΉ ΠΈ Π±Π°Π³ΠΎΠ² Π±Ρ‹Π»ΠΈ исправлСны.

Π˜Ρ‚Π°ΠΊ, ΠΏΠ΅Ρ€Π΅ΠΉΠ΄Π΅ΠΌ Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΊ самой ΡƒΡ‚ΠΈΠ»ΠΈΡ‚Π΅ SQLIndexManager.

ΠŸΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ написано Π½Π° языкС C# .NET Framework 4.5 Π² Visual Studio 2017 ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ DevExpress для Ρ„ΠΎΡ€ΠΌ:

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

ΠΈ выглядит ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ:

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

ВсС запросы Ρ„ΠΎΡ€ΠΌΠΈΡ€ΡƒΡŽΡ‚ΡΡ Π² ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΡ… Ρ„Π°ΠΉΠ»Π°Ρ…:

  1. Index
  2. Query
  3. QueryEngine
  4. ServerInfo

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

ΠŸΡ€ΠΈ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΈ ΠΊ Π±Π°Π·Π΅ Π΄Π°Π½Π½Ρ‹Ρ… ΠΈ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠ΅ запросов ΠΊ Π‘Π£Π‘Π”, ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ подписываСтся ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ:

ApplicationName=”SQLIndexManager”

ΠŸΡ€ΠΈ запускС прилоТСния откроСтся модальноС ΠΎΠΊΠ½ΠΎ Π½Π° Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ:
ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

Π—Π΄Π΅ΡΡŒ ΠΏΠΎΠΊΠ° Π½Π΅ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ ΠΏΠΎΠ΄Π³Ρ€ΡƒΠ·ΠΊΠ° ΠΏΠΎΠ»Π½ΠΎΠ³ΠΎ списка всСх экзСмпляров MS SQL Server, доступных ΠΏΠΎ Π»ΠΎΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΌ сСтям.

Π’Π°ΠΊΠΆΠ΅ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ½ΠΎ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΊΡ€Π°ΠΉΠ½Π΅ΠΉ Π»Π΅Π²ΠΎΠΉ ΠΊΠ½ΠΎΠΏΠΊΠΈ Π½Π° Π³Π»Π°Π²Π½ΠΎΠΌ мСню:

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

Π”Π°Π»Π΅Π΅ запустятся ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ запросы ΠΊ Π‘Π£Π‘Π”:

  1. ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ ΠΎ Π‘Π£Π‘Π”
    SELECT ProductLevel  = SERVERPROPERTY('ProductLevel')
         , Edition       = SERVERPROPERTY('Edition')
         , ServerVersion = SERVERPROPERTY('ProductVersion')
         , IsSysAdmin    = CAST(IS_SRVROLEMEMBER('sysadmin') AS BIT)
    

  2. ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ списка доступных Π±Π°Π· Π΄Π°Π½Π½Ρ‹Ρ… с ΠΈΡ… ΠΊΡ€Π°Ρ‚ΠΊΠΈΠΌΠΈ свойствами
    SELECT DatabaseName = t.[name]
         , d.DataSize
         , DataUsedSize  = CAST(NULL AS BIGINT)
         , d.LogSize
         , LogUsedSize   = CAST(NULL AS BIGINT)
         , RecoveryModel = t.recovery_model_desc
         , LogReuseWait  = t.log_reuse_wait_desc
    FROM sys.databases t WITH(NOLOCK)
    LEFT JOIN (
        SELECT [database_id]
             , DataSize = SUM(CASE WHEN [type] = 0 THEN CAST(size AS BIGINT) END)
             , LogSize  = SUM(CASE WHEN [type] = 1 THEN CAST(size AS BIGINT) END)
        FROM sys.master_files WITH(NOLOCK)
        GROUP BY [database_id]
    ) d ON d.[database_id] = t.[database_id]
    WHERE t.[state] = 0
        AND t.[database_id] != 2
        AND ISNULL(HAS_DBACCESS(t.[name]), 1) = 1
    

ПослС выполнСния Π²Ρ‹ΡˆΠ΅ΡƒΠΊΠ°Π·Π°Π½Π½Ρ‹Ρ… скриптов появится ΠΎΠΊΠ½ΠΎ, содСрТащСС ΠΊΡ€Π°Ρ‚ΠΊΡƒΡŽ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ Π±Π°Π·Π°Ρ… Π΄Π°Π½Π½Ρ‹Ρ… Π²Ρ‹Π±Ρ€Π°Π½Π½ΠΎΠ³ΠΎ экзСмпляра MS SQL Server:

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

Π‘Ρ‚ΠΎΠΈΡ‚ ΠΎΡ‚ΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½Π½Π°Ρ информация показываСтся, исходя ΠΈΠ· ΠΏΡ€Π°Π². Если Π΅ΡΡ‚ΡŒ sysadmin, Ρ‚ΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π²Ρ‹Π±ΠΈΡ€Π°Ρ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ ΠΈΠ· прСдставлСния sys.master_files. Если Ρ‚Π°ΠΊΠΈΡ… ΠΏΡ€Π°Π² Π½Π΅Ρ‚, Ρ‚ΠΎ просто возвращаСтся мСньшС Π΄Π°Π½Π½Ρ‹Ρ…, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π½Π΅ Π·Π°ΠΌΠ΅Π΄Π»ΡΡ‚ΡŒ запрос.

Π—Π΄Π΅ΡΡŒ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ ΠΈΠ½Ρ‚Π΅Ρ€Π΅ΡΡƒΡŽΡ‰ΠΈΠ΅ Π±Π°Π·Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ… ΠΈ Π½Π°ΠΆΠ°Ρ‚ΡŒ Π½Π° ΠΊΠ½ΠΎΠΏΠΊΡƒ β€œΠžΠšβ€.

Π”Π°Π»Π΅Π΅ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΉ скрипт для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ Π²Ρ‹Π±Ρ€Π°Π½Π½ΠΎΠΉ Π±Π°Π·Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ… для Π°Π½Π°Π»ΠΈΠ·Π° состояния индСксов:

Анализ состояния индСксов

declare @Fragmentation float=15;
declare @MinIndexSize bigint=768;
declare @MaxIndexSize bigint=1048576;
declare @PreDescribeSize bigint=32768;
SET NOCOUNT ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
IF OBJECT_ID('tempdb.dbo.#AllocationUnits') IS NOT NULL
DROP TABLE #AllocationUnits
CREATE TABLE #AllocationUnits (
ContainerID   BIGINT PRIMARY KEY
, ReservedPages BIGINT NOT NULL
, UsedPages     BIGINT NOT NULL
)
INSERT INTO #AllocationUnits (ContainerID, ReservedPages, UsedPages)
SELECT [container_id]
, SUM([total_pages])
, SUM([used_pages])
FROM sys.allocation_units WITH(NOLOCK)
GROUP BY [container_id]
HAVING SUM([total_pages]) BETWEEN @MinIndexSize AND @MaxIndexSize
IF OBJECT_ID('tempdb.dbo.#ExcludeList') IS NOT NULL
DROP TABLE #ExcludeList
CREATE TABLE #ExcludeList (ID INT PRIMARY KEY)
INSERT INTO #ExcludeList
SELECT [object_id]
FROM sys.objects WITH(NOLOCK)
WHERE [type] IN ('V', 'U')
AND ( [is_ms_shipped] = 1 )
IF OBJECT_ID('tempdb.dbo.#Partitions') IS NOT NULL
DROP TABLE #Partitions
SELECT [object_id]
, [index_id]
, [partition_id]
, [partition_number]
, [rows]
, [data_compression]
INTO #Partitions
FROM sys.partitions WITH(NOLOCK)
WHERE [object_id] > 255
AND [rows] > 0
AND [object_id] NOT IN (SELECT * FROM #ExcludeList)
IF OBJECT_ID('tempdb.dbo.#Indexes') IS NOT NULL
DROP TABLE #Indexes
CREATE TABLE #Indexes (
ObjectID         INT NOT NULL
, IndexID          INT NOT NULL
, IndexName        SYSNAME NULL
, PagesCount       BIGINT NOT NULL
, UnusedPagesCount BIGINT NOT NULL
, PartitionNumber  INT NOT NULL
, RowsCount        BIGINT NOT NULL
, IndexType        TINYINT NOT NULL
, IsAllowPageLocks BIT NOT NULL
, DataSpaceID      INT NOT NULL
, DataCompression  TINYINT NOT NULL
, IsUnique         BIT NOT NULL
, IsPK             BIT NOT NULL
, FillFactorValue  INT NOT NULL
, IsFiltered       BIT NOT NULL
, PRIMARY KEY (ObjectID, IndexID, PartitionNumber)
)
INSERT INTO #Indexes
SELECT ObjectID         = i.[object_id]
, IndexID          = i.index_id
, IndexName        = i.[name]
, PagesCount       = a.ReservedPages
, UnusedPagesCount = CASE WHEN ABS(a.ReservedPages - a.UsedPages) > 32 THEN a.ReservedPages - a.UsedPages ELSE 0 END
, PartitionNumber  = p.[partition_number]
, RowsCount        = ISNULL(p.[rows], 0)
, IndexType        = i.[type]
, IsAllowPageLocks = i.[allow_page_locks]
, DataSpaceID      = i.[data_space_id]
, DataCompression  = p.[data_compression]
, IsUnique         = i.[is_unique]
, IsPK             = i.[is_primary_key]
, FillFactorValue  = i.[fill_factor]
, IsFiltered       = i.[has_filter]
FROM #AllocationUnits a
JOIN #Partitions p ON a.ContainerID = p.[partition_id]
JOIN sys.indexes i WITH(NOLOCK) ON i.[object_id] = p.[object_id] AND p.[index_id] = i.[index_id] 
WHERE i.[type] IN (0, 1, 2, 5, 6)
AND i.[object_id] > 255
DECLARE @files TABLE (ID INT PRIMARY KEY)
INSERT INTO @files
SELECT DISTINCT [data_space_id]
FROM sys.database_files WITH(NOLOCK)
WHERE [state] != 0
AND [type] = 0
IF @@ROWCOUNT > 0 BEGIN
DELETE FROM i
FROM #Indexes i
LEFT JOIN sys.destination_data_spaces dds WITH(NOLOCK) ON i.DataSpaceID = dds.[partition_scheme_id] AND i.PartitionNumber = dds.[destination_id]
WHERE ISNULL(dds.[data_space_id], i.DataSpaceID) IN (SELECT * FROM @files)
END
DECLARE @DBID   INT
, @DBNAME SYSNAME
SET @DBNAME = DB_NAME()
SELECT @DBID = [database_id]
FROM sys.databases WITH(NOLOCK)
WHERE [name] = @DBNAME
IF OBJECT_ID('tempdb.dbo.#Fragmentation') IS NOT NULL
DROP TABLE #Fragmentation
CREATE TABLE #Fragmentation (
ObjectID         INT NOT NULL
, IndexID          INT NOT NULL
, PartitionNumber  INT NOT NULL
, Fragmentation    FLOAT NOT NULL
, PRIMARY KEY (ObjectID, IndexID, PartitionNumber)
)
INSERT INTO #Fragmentation (ObjectID, IndexID, PartitionNumber, Fragmentation)
SELECT i.ObjectID
, i.IndexID
, i.PartitionNumber
, r.[avg_fragmentation_in_percent]
FROM #Indexes i
CROSS APPLY sys.dm_db_index_physical_stats(@DBID, i.ObjectID, i.IndexID, i.PartitionNumber, 'LIMITED') r
WHERE i.PagesCount <= @PreDescribeSize
AND r.[index_level] = 0
AND r.[alloc_unit_type_desc] = 'IN_ROW_DATA'
AND i.IndexType IN (0, 1, 2)
IF OBJECT_ID('tempdb.dbo.#Columns') IS NOT NULL
DROP TABLE #Columns
CREATE TABLE #Columns (
ObjectID     INT NOT NULL
, ColumnID     INT NOT NULL
, ColumnName   SYSNAME NULL
, SystemTypeID TINYINT NULL
, IsSparse     BIT
, IsColumnSet  BIT
, MaxLen       INT
, PRIMARY KEY (ObjectID, ColumnID)
)
INSERT INTO #Columns
SELECT ObjectID     = [object_id]
, ColumnID     = [column_id]
, ColumnName   = [name]
, SystemTypeID = [system_type_id]
, IsSparse     = [is_sparse]
, IsColumnSet  = [is_column_set]
, MaxLen       = [max_length]
FROM sys.columns WITH(NOLOCK)
WHERE [object_id] IN (SELECT DISTINCT i.ObjectID FROM #Indexes i)
IF OBJECT_ID('tempdb.dbo.#IndexColumns') IS NOT NULL
DROP TABLE #IndexColumns
CREATE TABLE #IndexColumns (
ObjectID   INT NOT NULL
, IndexID    INT NOT NULL
, OrderID    INT NOT NULL
, ColumnID   INT NOT NULL
, IsIncluded BIT NOT NULL
, PRIMARY KEY (ObjectID, IndexID, ColumnID)
)
INSERT INTO #IndexColumns
SELECT ObjectID   = [object_id]
, IndexID    = [index_id]
, OrderID    = CASE WHEN [is_included_column] = 0 THEN [key_ordinal] ELSE [index_column_id] END
, ColumnID   = [column_id]
, IsIncluded = ISNULL([is_included_column], 0)
FROM sys.index_columns ic WITH(NOLOCK)
WHERE EXISTS(
SELECT *
FROM #Indexes i
WHERE i.ObjectID = ic.[object_id]
AND i.IndexID = ic.[index_id]
AND i.IndexType IN (1, 2)
)
IF OBJECT_ID('tempdb.dbo.#Lob') IS NOT NULL
DROP TABLE #Lob
CREATE TABLE #Lob (
ObjectID    INT NOT NULL
, IndexID     INT NOT NULL
, IsLobLegacy BIT
, IsLob       BIT
, PRIMARY KEY (ObjectID, IndexID)
)
INSERT INTO #Lob (ObjectID, IndexID, IsLobLegacy, IsLob)
SELECT c.ObjectID
, IndexID     = ISNULL(i.IndexID, 1)
, IsLobLegacy = MAX(CASE WHEN c.SystemTypeID IN (34, 35, 99) THEN 1 END)
, IsLob       = 0
FROM #Columns c
LEFT JOIN #IndexColumns i ON c.ObjectID = i.ObjectID AND c.ColumnID = i.ColumnID
WHERE c.SystemTypeID IN (34, 35, 99)
GROUP BY c.ObjectID
, i.IndexID
IF OBJECT_ID('tempdb.dbo.#Sparse') IS NOT NULL
DROP TABLE #Sparse
CREATE TABLE #Sparse (ObjectID INT PRIMARY KEY)
INSERT INTO #Sparse
SELECT DISTINCT ObjectID
FROM #Columns
WHERE IsSparse = 1
OR IsColumnSet = 1
IF OBJECT_ID('tempdb.dbo.#AggColumns') IS NOT NULL
DROP TABLE #AggColumns
CREATE TABLE #AggColumns (
ObjectID        INT NOT NULL
, IndexID         INT NOT NULL
, IndexColumns    NVARCHAR(MAX)
, IncludedColumns NVARCHAR(MAX)
, PRIMARY KEY (ObjectID, IndexID)
)
INSERT INTO #AggColumns
SELECT t.ObjectID
, t.IndexID
, IndexColumns = STUFF((
SELECT ', [' + c.ColumnName + ']'
FROM #IndexColumns i
JOIN #Columns c ON i.ObjectID = c.ObjectID AND i.ColumnID = c.ColumnID
WHERE i.ObjectID = t.ObjectID
AND i.IndexID = t.IndexID
AND i.IsIncluded = 0
ORDER BY i.OrderID
FOR XML PATH(''), TYPE).value('(./text())[1]', 'NVARCHAR(MAX)'), 1, 2, '')
, IncludedColumns = STUFF((
SELECT ', [' + c.ColumnName + ']'
FROM #IndexColumns i
JOIN #Columns c ON i.ObjectID = c.ObjectID AND i.ColumnID = c.ColumnID
WHERE i.ObjectID = t.ObjectID
AND i.IndexID = t.IndexID
AND i.IsIncluded = 1
ORDER BY i.OrderID
FOR XML PATH(''), TYPE).value('(./text())[1]', 'NVARCHAR(MAX)'), 1, 2, '')
FROM (
SELECT DISTINCT ObjectID, IndexID
FROM #Indexes
WHERE IndexType IN (1, 2)
) t
SELECT i.ObjectID
, i.IndexID
, i.IndexName
, ObjectName       = o.[name]
, SchemaName       = s.[name]
, i.PagesCount
, i.UnusedPagesCount
, i.PartitionNumber
, i.RowsCount
, i.IndexType
, i.IsAllowPageLocks
, u.TotalWrites
, u.TotalReads
, u.TotalSeeks
, u.TotalScans
, u.TotalLookups
, u.LastUsage
, i.DataCompression
, f.Fragmentation
, IndexStats       = STATS_DATE(i.ObjectID, i.IndexID)
, IsLobLegacy      = ISNULL(lob.IsLobLegacy, 0)
, IsLob            = ISNULL(lob.IsLob, 0)
, IsSparse         = CAST(CASE WHEN p.ObjectID IS NULL THEN 0 ELSE 1 END AS BIT)
, IsPartitioned    = CAST(CASE WHEN dds.[data_space_id] IS NOT NULL THEN 1 ELSE 0 END AS BIT)
, FileGroupName    = fg.[name]
, i.IsUnique
, i.IsPK
, i.FillFactorValue
, i.IsFiltered
, a.IndexColumns
, a.IncludedColumns
FROM #Indexes i
JOIN sys.objects o WITH(NOLOCK) ON o.[object_id] = i.ObjectID
JOIN sys.schemas s WITH(NOLOCK) ON s.[schema_id] = o.[schema_id]
LEFT JOIN #AggColumns a ON a.ObjectID = i.ObjectID AND a.IndexID = i.IndexID
LEFT JOIN #Sparse p ON p.ObjectID = i.ObjectID
LEFT JOIN #Fragmentation f ON f.ObjectID = i.ObjectID AND f.IndexID = i.IndexID AND f.PartitionNumber = i.PartitionNumber
LEFT JOIN (
SELECT ObjectID      = [object_id]
, IndexID       = [index_id]
, TotalWrites   = NULLIF([user_updates], 0)
, TotalReads    = NULLIF([user_seeks] + [user_scans] + [user_lookups], 0)
, TotalSeeks    = NULLIF([user_seeks], 0)
, TotalScans    = NULLIF([user_scans], 0)
, TotalLookups  = NULLIF([user_lookups], 0)
, LastUsage     = (
SELECT MAX(dt)
FROM (
VALUES ([last_user_seek])
, ([last_user_scan])
, ([last_user_lookup])
, ([last_user_update])
) t(dt)
)
FROM sys.dm_db_index_usage_stats WITH(NOLOCK)
WHERE [database_id] = @DBID
) u ON i.ObjectID = u.ObjectID AND i.IndexID = u.IndexID
LEFT JOIN #Lob lob ON lob.ObjectID = i.ObjectID AND lob.IndexID = i.IndexID
LEFT JOIN sys.destination_data_spaces dds WITH(NOLOCK) ON i.DataSpaceID = dds.[partition_scheme_id] AND i.PartitionNumber = dds.[destination_id]
JOIN sys.filegroups fg WITH(NOLOCK) ON ISNULL(dds.[data_space_id], i.DataSpaceID) = fg.[data_space_id] 
WHERE o.[type] IN ('V', 'U')
AND (
f.Fragmentation >= @Fragmentation
OR
i.PagesCount > @PreDescribeSize
OR
i.IndexType IN (5, 6)
)

Как Π²ΠΈΠ΄Π½ΠΎ ΠΈΠ· самих запросов, достаточно часто ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ΡΡ Π²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹. Π­Ρ‚ΠΎ сдСлано для Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π½Π΅ Π±Ρ‹Π»ΠΎ рСкомпиляций, ΠΈ Π² случаС большой схСмы, ΠΏΠ»Π°Π½ ΠΌΠΎΠ³ Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒΡΡ ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΏΡ€ΠΈ вставкС Π΄Π°Π½Π½Ρ‹Ρ…, Ρ‚ ΠΊ вставка с Ρ‚Π°Π±Π»ΠΈΡ‡Π½Ρ‹ΠΌΠΈ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΌΠΈ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Π° Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π² ΠΎΠ΄ΠΈΠ½ ΠΏΠΎΡ‚ΠΎΠΊ.

ПослС выполнСния Π²Ρ‹ΡˆΠ΅ΡƒΠΊΠ°Π·Π°Π½Π½ΠΎΠ³ΠΎ скрипта появится ΠΎΠΊΠ½ΠΎ с Ρ‚Π°Π±Π»ΠΈΡ†Π΅ΠΉ индСксов:

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

Π’Π°ΠΊΠΆΠ΅ здСсь ΠΌΠΎΠΆΠ½ΠΎ вывСсти ΠΈ Π΄Ρ€ΡƒΠ³ΡƒΡŽ Π΄Π΅Ρ‚Π°Π»ΡŒΠ½ΡƒΡŽ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ, Ρ‚Π°ΠΊΡƒΡŽ ΠΊΠ°ΠΊ:

  1. Π±Π°Π·Π° Π΄Π°Π½Π½Ρ‹Ρ…
  2. количСство сСкций
  3. Π΄Π°Ρ‚Π° ΠΈ врСмя послСднСго обращСния
  4. сТатиС
  5. файловая Π³Ρ€ΡƒΠΏΠΏΠ°

ΠΈ Ρ‚. Π΄.
Π‘Π°ΠΌΠΈ ΠΊΠΎΠ»ΠΎΠ½ΠΊΠΈ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΡΡ‚Ρ€Π°ΠΈΠ²Π°Ρ‚ΡŒ:

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

Π’ ячСйках ΠΊΠΎΠ»ΠΎΠ½ΠΊΠΈ Fix ΠΌΠΎΠΆΠ½ΠΎ Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ ΠΊΠ°ΠΊΠΎΠ΅ дСйствиС Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ ΠΏΡ€ΠΈ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ. Π’Π°ΠΊΠΆΠ΅ ΠΏΡ€ΠΈ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠΈ сканирования дСйствиС ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ выбираСтся Π½Π° основании Π²Ρ‹Π±Ρ€Π°Π½Π½Ρ‹Ρ… настроСк:

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

НСобходимо Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ Π½ΡƒΠΆΠ½Ρ‹Π΅ индСксы для ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ.

Π‘ ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Π³Π»Π°Π²Π½ΠΎΠ³ΠΎ мСню ΠΌΠΎΠΆΠ½ΠΎ ΠΊΠ°ΠΊ ΡΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ скрипт (эта ΠΆΠ΅ ΠΊΠ½ΠΎΠΏΠΊΠ° запускаСт сам процСсс ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ индСксов):

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

Ρ‚Π°ΠΊ ΠΈ ΡΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ Π² Ρ€Π°Π·Π½Ρ‹Π΅ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Ρ‹ (эта ΠΆΠ΅ ΠΊΠ½ΠΎΠΏΠΊΠ° позволяСт ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ Π΄Π΅Ρ‚Π°Π»ΡŒΠ½Ρ‹Π΅ настройки для Π°Π½Π°Π»ΠΈΠ·Π° ΠΈ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ индСксов):

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

Π’Π°ΠΊΠΆΠ΅ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΌΠΎΠΆΠ½ΠΎ ΠΎΠ±Π½ΠΎΠ²ΠΈΡ‚ΡŒ, Π½Π°ΠΆΠ°Π² Π½Π° Ρ‚Ρ€Π΅Ρ‚ΡŒΡŽ ΠΊΠ½ΠΎΠΏΠΊΡƒ слСва Π² Π³Π»Π°Π²Π½ΠΎΠΌ мСню рядом с Π»ΡƒΠΏΠΎΠΉ.

Кнопка с Π»ΡƒΠΏΠΎΠΉ позволяСт Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ Π½ΡƒΠΆΠ½Ρ‹Π΅ Π±Π°Π·Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ… для рассмотрСния.

ΠŸΠΎΠ»Π½ΠΎΡ†Π΅Π½Π½ΠΎΠΉ справочной систСмы Π½Π° Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ Π½Π΅Ρ‚. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ Π½Π°ΠΆΠ°Ρ‚ΠΈΠ΅ Π½Π° ΠΊΠ½ΠΎΠΏΠΊΡƒ β€œ?” Π²Ρ‹Π·ΠΎΠ²Π΅Ρ‚ просто появлСниС модального ΠΎΠΊΠ½Π°, содСрТащСго ΠΎΡΠ½ΠΎΠ²Π½ΡƒΡŽ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ½ΠΎΠΌ ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚Π΅:

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

Помимо всСго Π²Ρ‹ΡˆΠ΅ΠΎΠΏΠΈΡΠ°Π½Π½ΠΎΠ³ΠΎ Π² Π³Π»Π°Π²Π½ΠΎΠΌ мСню Π΅ΡΡ‚ΡŒ строка поиска:

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

ΠŸΡ€ΠΈ запускС процСсса ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ индСксов:

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

Π’Π°ΠΊΠΆΠ΅ Π²Π½ΠΈΠ·Ρƒ ΠΎΠΊΠ½Π° ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡ€ΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π»ΠΎΠ³ выполняСмых дСйствий:

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

Π’ ΠΎΠΊΠ½Π΅ Π΄Π΅Ρ‚Π°Π»ΡŒΠ½Ρ‹Ρ… настроСк Π°Π½Π°Π»ΠΈΠ·Π° ΠΈ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ индСксов ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ Π±ΠΎΠ»Π΅Π΅ Ρ‚ΠΎΠ½ΠΊΠΈΠ΅ ΠΎΠΏΡ†ΠΈΠΈ:

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

ПоТСлания ΠΊ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡŽ:

  1. ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹ΠΌ Π²Ρ‹Π±ΠΎΡ€ΠΎΡ‡Π½ΠΎ ΠΎΠ±Π½ΠΎΠ²Π»ΡΡ‚ΡŒ статистики Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ для индСксов ΠΈ Ρ‚Π°ΠΊΠΆΠ΅ Ρ€Π°Π·Π½Ρ‹ΠΌΠΈ способами (ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ ΠΎΠ±Π½ΠΎΠ²Π»ΡΡ‚ΡŒ ΠΈΠ»ΠΈ частично)
  2. ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹ΠΌ Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π²Ρ‹Π±ΠΈΡ€Π°Ρ‚ΡŒ Π‘Π”, Π½ΠΎ ΠΈ Ρ€Π°Π·Π½Ρ‹Π΅ сСрвСра (это ΠΎΡ‡Π΅Π½ΡŒ ΡƒΠ΄ΠΎΠ±Π½ΠΎ, ΠΊΠΎΠ³Π΄Π° ΠΌΠ½ΠΎΠ³ΠΎ экзСмпляров MS SQL Server)
  3. для большСй гибкости Π² использовании прСдлагаСтся ΠΎΠ±Π΅Ρ€Π½ΡƒΡ‚ΡŒ ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ Π² Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ, ΠΈ вывСсти Π² ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ PowerShell, ΠΊΠ°ΠΊ это сдСлано, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, здСсь:
  4. dbatools.io/commands
  5. ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹ΠΌ ΡΠΎΡ…Ρ€Π°Π½ΡΡ‚ΡŒ ΠΈ ΠΈΠ·ΠΌΠ΅Π½ΡΡ‚ΡŒ ΠΏΠ΅Ρ€ΡΠΎΠ½Π°Π»ΡŒΠ½Ρ‹Π΅ настройки ΠΊΠ°ΠΊ для всСго прилоТСния, Ρ‚Π°ΠΊ ΠΈ Π² случаС нСобходимости для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ экзСмпляра MS SQL Server ΠΈ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ Π±Π°Π·Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ…
  6. ΠΈΠ· ΠΏ.2 ΠΈ 4 Π²Ρ‹Ρ‚Π΅ΠΊΠ°Π΅Ρ‚ ΠΏΠΎΠΆΠ΅Π»Π°Π½ΠΈΠ΅ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Π³Ρ€ΡƒΠΏΠΏΡ‹ ΠΏΠΎ Π±Π°Π·Π°ΠΌ Π΄Π°Π½Π½Ρ‹Ρ… ΠΈ Π³Ρ€ΡƒΠΏΠΏΡ‹ ΠΏΠΎ экзСмплярам MS SQL Server, для ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… настройки ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²Ρ‹Π΅
  7. ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ поиск Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ‚ΠΎΠ² индСксов (ΠΏΠΎΠ»Π½Ρ‹Ρ… ΠΈ Π½Π΅ΠΏΠΎΠ»Π½Ρ‹Ρ…, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π»ΠΈΠ±ΠΎ нСсильно ΠΎΡ‚Π»ΠΈΡ‡Π°ΡŽΡ‚ΡΡ, Π»ΠΈΠ±ΠΎ ΠΎΡ‚Π»ΠΈΡ‡Π°ΡŽΡ‚ΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΠΎ Π²ΠΊΠ»ΡŽΡ‡Π΅Π½Π½Ρ‹ΠΌ ΠΊΠΎΠ»ΠΎΠ½ΠΊΠ°ΠΌ)
  8. Ρ‚ ΠΊ SQLIndexManager ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ для Π‘Π£Π‘Π” MS SQL Server, Ρ‚ΠΎ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΎΡ‚Ρ€Π°Π·ΠΈΡ‚ΡŒ это Π² Π½Π°Π·Π²Π°Π½ΠΈΠΈ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ: SQLIndexManager for MS SQL Server
  9. всС части прилоТСния Π½Π΅ GUI вынСсти Π² ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Π΅ ΠΌΠΎΠ΄ΡƒΠ»ΠΈ ΠΈ ΠΏΠ΅Ρ€Π΅ΠΏΠΈΡΠ°Ρ‚ΡŒ Π½Π° .NET Core 2.1

На ΠΌΠΎΠΌΠ΅Π½Ρ‚ написания ΡΡ‚Π°Ρ‚ΡŒΠΈ ΠΏ.6 ΠΈΠ· ΠΏΠΎΠΆΠ΅Π»Π°Π½ΠΈΠΉ Π°ΠΊΡ‚ΠΈΠ²Π½ΠΎ разрабатываСтся ΠΈ ΡƒΠΆΠ΅ Π΅ΡΡ‚ΡŒ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° Π² Π²ΠΈΠ΄Π΅ поиска ΠΏΠΎΠ»Π½Ρ‹Ρ… ΠΈ ΠΏΠΎΠ΄ΠΎΠ±Π½Ρ‹Ρ… Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ‚ΠΎΠ²:

ΠžΠ±Π·ΠΎΡ€ бСсплатного инструмСнта SQLIndexManager

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊΠΈ

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com