Famerenana ny fitaovana maimaim-poana SQLIndexManager
Araka ny fantatrao, ny index dia manana anjara toerana lehibe ao amin'ny DBMS, manome fikarohana haingana amin'ireo rakitra ilaina. Izany no antony maha-zava-dehibe ny fanompoana azy ireo ara-potoana. Betsaka ny zavatra nosoratana momba ny fanadihadiana sy ny fanatsarana, anisan'izany ny Internet. Ohatra, ity lohahevitra ity dia nodinihina vao haingana tao Ity publication ity.
Betsaka ny vahaolana karamaina sy maimaim-poana amin'izany. Ohatra, misy efa vita fanapahan-kevitra, mifototra amin'ny fomba fanatsarana ny fanondroana adaptive.
Manaraka, andeha hojerentsika ny fitaovana maimaim-poana SQLIndexManager, nosoratan'i AlanDenton.
Ny fahasamihafana ara-teknika lehibe eo amin'ny SQLIndexManager sy ny analogues hafa dia omen'ny mpanoratra ny tenany eto ΠΈ eto.
Ato amin'ity lahatsoratra ity, hijery ivelany ny tetikasa sy ny fahaiza-miasa amin'ity vahaolana rindrambaiko ity.
Miady hevitra momba ity utility ity eto.
Rehefa nandeha ny fotoana, dia nahitsy ny ankamaroan'ny fanehoan-kevitra sy ny bug.
Noho izany, andeha isika hifindra any amin'ny fampiasa SQLIndexManager.
Ny fampiharana dia voasoratra ao amin'ny C# .NET Framework 4.5 ao amin'ny Visual Studio 2017 ary mampiasa DevExpress amin'ny endrika:
ary toa izao:
Ny fangatahana rehetra dia noforonina amin'ireto rakitra manaraka ireto:
Index
QUERY
QueryEngine
ServerInfo
Rehefa mifandray amin'ny angon-drakitra ary mandefa fanontaniana amin'ny DBMS, dia sonia toy izao manaraka izao ny fampiharana:
ApplicationName=βSQLIndexManagerβ
Rehefa manomboka ny fampiharana ianao, dia hisokatra ny fikandrana modal hanampiana fifandraisana:
Eto, tsy mbola mandeha ny fametrahana lisitra feno amin'ireo tranga MS SQL Server azo idirana amin'ny tambajotra eo an-toerana.
Azonao atao koa ny manampy fifandraisana amin'ny alΓ lan'ny bokotra havia indrindra amin'ny menio lehibe:
Mahazoa lisitry ny angon-drakitra misy miaraka amin'ny fananany fohifohy
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
Aorian'ny fanatanterahana ireo sora-baventy etsy ambony dia hiseho ny varavarankely misy fampahalalana fohy momba ny angon-drakitra momba ny ohatra voafantina an'ny MS SQL Server:
Tsara ny manamarika fa ny fampahalalana miitatra dia aseho amin'ny alΓ lan'ny zo. Raha misy sysadmin, dia afaka misafidy angona avy amin'ny fijery ianao sys.master_files. Raha tsy misy zo toy izany, dia kely kokoa ny angona averina fotsiny mba tsy hampiadana ny fangatahana.
Eto ianao dia mila misafidy ny angon-drakitra mahaliana ary tsindrio ny bokotra "OK".
Manaraka, ity script manaraka ity dia hotanterahina ho an'ny angon-drakitra voafantina tsirairay mba hamakafaka ny toetry ny index:
Fandinihana ny satan'ny fanondro
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)
)
Araka ny hita avy amin'ny fanontaniana, ny latabatra vonjimaika dia ampiasaina matetika. Izany dia atao mba tsy hisian'ny recompilations, ary raha misy drafitra lehibe, ny drafitra dia azo amboarina amin'ny parallèle rehefa mampiditra angon-drakitra, satria ny fampidirana ny variables latabatra dia azo atao amin'ny kofehy iray ihany.
Aorian'ny fanatanterahana ny script etsy ambony dia hiseho ny varavarankely misy tabilao index:
Azonao atao koa ny mampiseho fampahalalana amin'ny antsipiriany hafa eto, toy ny:
banky angona
isan'ny fizarana
daty sy ora niantsoana farany
famatrarana
filegroup
i t. d.
Ny mpandahateny dia azo atao manokana:
Ao amin'ny sela ao amin'ny tsanganana Fix, azonao atao ny misafidy izay hetsika hatao mandritra ny fanatsarana. Ary koa, rehefa vita ny scan, dia misy hetsika default voafantina mifototra amin'ny toe-javatra voafantina:
Tsy maintsy misafidy ny fanondroana tianao hokarakaraina ianao.
Amin'ny fampiasana ny menio lehibe, azonao atao ny mitahiry ny script (io bokotra io ihany no manomboka ny fizotran'ny fanatsarana ny index):
ary tehirizo ny latabatra amin'ny endrika samihafa (ny bokotra iray ihany dia ahafahanao manokatra fika amin'ny antsipiriany ho an'ny famakafakana sy fanatsarana ny indeks):
Azonao atao koa ny manavao ny vaovao amin'ny fipihana ny bokotra fahatelo eo ankavia amin'ny sakafo lehibe eo akaikin'ny fitaratra lehibe.
Ny bokotra misy vera fanamafisam-peo dia ahafahanao misafidy ny angon-drakitra tiana hodinihina.
Tsy misy rafitra fanampiana feno amin'izao fotoana izao. Noho izany, tsindrio ny bokotra "?" dia hahatonga fikandrana modaly hiseho fotsiny misy fampahalalana fototra momba ny vokatra rindrambaiko:
Ho fanampin'ny zavatra rehetra voalaza etsy ambony, misy bara fitadiavana ny sakafo fototra:
Rehefa manomboka ny fizotran'ny optimization index:
Azonao atao ihany koa ny mijery ny diarin'ny hetsika natao eo amin'ny faran'ny varavarankely:
Ao amin'ny varavarankelin'ny toe-javatra amin'ny antsipiriany ho an'ny famakafakana sy ny fanatsarana ny index, dia azonao atao ny manitsy safidy an-kolaka kokoa:
Fangatahana fangatahana:
ataovy azo atao ny manavao ny antontan'isa tsy ho an'ny index ihany fa amin'ny fomba samihafa (fanavaozana tanteraka na ampahany)
ataovy azo atao tsy ny mifidy angon-drakitra fotsiny, fa koa ireo mpizara samihafa (tena mety izany rehefa misy tranga maro amin'ny MS SQL Server)
Ho an'ny flexibility bebe kokoa amin'ny fampiasana, dia soso-kevitra ny hamehezana ny baiko ao amin'ny tranomboky ary hamoaka azy ireo amin'ny baiko PowerShell, toy ny atao, ohatra, eto:
Ataovy azo atao ny mitahiry sy manova ny toe-javatra manokana ho an'ny fampiharana manontolo ary, raha ilaina, ho an'ny MS SQL Server tsirairay sy ny tahiry tsirairay.
Avy amin'ny teboka 2 sy 4, dia manaraka izany fa te hamorona vondrona amin'ny angon-drakitra sy vondrona amin'ny MS SQL Server ohatra ianao, izay mitovy ny toe-javatra.
karohy fanondroana dika mitovy (feno sy tsy feno, izay samy hafa kely na tsy mitovy afa-tsy amin'ireo tsanganana tafiditra)
Satria ny SQLIndexManager dia tsy ampiasaina afa-tsy amin'ny MS SQL Server DBMS, dia ilaina ny maneho izany amin'ny anarana, ohatra, toy izao manaraka izao: SQLIndexManager ho an'ny MS SQL Server
Alefaso any amin'ny maody misaraka avokoa ny ampahany tsy GUI amin'ny fampiharana ary soraty ao amin'ny .NET Core 2.1.
Amin'ny fotoana anoratana, ny laharana faha-6 amin'ireo faniriana dia eo am-pamolavolana mavitrika ary efa misy ny fanohanana amin'ny endrika fitadiavana ireo dika mitovy feno sy mitovy: