the missing hyphen in the folder name was bothering me...
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,22 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# SQL Server Management Studio Solution File, Format Version 18.00
|
||||
VisualStudioVersion = 15.0.28307.421
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{4F2E2C19-372F-40D8-9FA7-9D2138C6997A}") = "OCTPDBA-365 Check index usage stats of [Entry] table", "OCTPDBA-365 Check index usage stats of [Entry] table.ssmssqlproj", "{E04A24E8-E4A1-425A-8196-6773BE0C4FB7}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Default|Default = Default|Default
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{E04A24E8-E4A1-425A-8196-6773BE0C4FB7}.Default|Default.ActiveCfg = Default
|
||||
{A53850FA-C396-4B1D-A94D-52E99FD87951}.Default|Default.ActiveCfg = Default
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {17EEE1F6-27D6-4E4A-B903-570D9844FE78}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0"?>
|
||||
<SqlWorkbenchSqlProject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Name="OCTPDBA-365 Check index usage stats of [Entry] table">
|
||||
<Items>
|
||||
<LogicalFolder Name="Connections" Type="2" Sorted="true">
|
||||
<Items>
|
||||
<ConnectionNode Name="(local):CENTRALINFRA\ua208700">
|
||||
<Created>2022-12-06T13:24:23.3133285+01:00</Created>
|
||||
<Type>SQL</Type>
|
||||
<Server>(local)</Server>
|
||||
<UserName />
|
||||
<Authentication>Windows Authentication</Authentication>
|
||||
<InitialDB />
|
||||
<LoginTimeout>30</LoginTimeout>
|
||||
<ExecutionTimeout>0</ExecutionTimeout>
|
||||
<ConnectionProtocol>NotSpecified</ConnectionProtocol>
|
||||
<ApplicationName>Microsoft SQL Server Management Studio - Query</ApplicationName>
|
||||
</ConnectionNode>
|
||||
<ConnectionNode Name="ama001aps\apssql:CENTRALINFRA\ua208700">
|
||||
<Created>2022-12-06T11:15:49.8449403+01:00</Created>
|
||||
<Type>SQL</Type>
|
||||
<Server>ama001aps\apssql</Server>
|
||||
<UserName />
|
||||
<Authentication>Windows Authentication</Authentication>
|
||||
<InitialDB />
|
||||
<LoginTimeout>30</LoginTimeout>
|
||||
<ExecutionTimeout>0</ExecutionTimeout>
|
||||
<ConnectionProtocol>NotSpecified</ConnectionProtocol>
|
||||
<ApplicationName>Microsoft SQL Server Management Studio - Query</ApplicationName>
|
||||
</ConnectionNode>
|
||||
<ConnectionNode Name="sun008aps.sunstore.ch\apssql:CENTRALINFRA\ua208700">
|
||||
<Created>2022-11-07T09:36:04.9485106+01:00</Created>
|
||||
<Type>SQL</Type>
|
||||
<Server>sun008aps.sunstore.ch\apssql</Server>
|
||||
<UserName />
|
||||
<Authentication>Windows Authentication</Authentication>
|
||||
<InitialDB />
|
||||
<LoginTimeout>30</LoginTimeout>
|
||||
<ExecutionTimeout>0</ExecutionTimeout>
|
||||
<ConnectionProtocol>NotSpecified</ConnectionProtocol>
|
||||
<ApplicationName>Microsoft SQL Server Management Studio - Query</ApplicationName>
|
||||
</ConnectionNode>
|
||||
</Items>
|
||||
</LogicalFolder>
|
||||
<LogicalFolder Name="Queries" Type="0" Sorted="true">
|
||||
<Items>
|
||||
<FileNode Name="idx usage with definition.sql">
|
||||
<AssociatedConnectionMoniker>8c91a03d-f9b4-46c0-a305-b5dcc79ff907:sun008aps.sunstore.ch\apssql:True</AssociatedConnectionMoniker>
|
||||
<AssociatedConnSrvName>sun008aps.sunstore.ch\apssql</AssociatedConnSrvName>
|
||||
<AssociatedConnUserName />
|
||||
<FullPath>idx usage with definition.sql</FullPath>
|
||||
</FileNode>
|
||||
<FileNode Name="maintain entry indexes.sql">
|
||||
<AssociatedConnectionMoniker>8c91a03d-f9b4-46c0-a305-b5dcc79ff907:(local):True</AssociatedConnectionMoniker>
|
||||
<AssociatedConnSrvName>(local)</AssociatedConnSrvName>
|
||||
<AssociatedConnUserName />
|
||||
<FullPath>maintain entry indexes.sql</FullPath>
|
||||
</FileNode>
|
||||
<FileNode Name="Search table in dbs.sql">
|
||||
<AssociatedConnectionMoniker />
|
||||
<AssociatedConnSrvName />
|
||||
<AssociatedConnUserName />
|
||||
<FullPath>Search table in dbs.sql</FullPath>
|
||||
</FileNode>
|
||||
</Items>
|
||||
</LogicalFolder>
|
||||
<LogicalFolder Name="Miscellaneous" Type="3" Sorted="true">
|
||||
<Items />
|
||||
</LogicalFolder>
|
||||
</Items>
|
||||
</SqlWorkbenchSqlProject>
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Server: HCIMON
|
||||
Format: HCI
|
||||
Business: OTHER
|
||||
type: PROD
|
||||
Version: No Arizona db on this instance
|
||||
|
||||
01.11.2022, TSC
|
||||
*/
|
||||
USE master;
|
||||
|
||||
DECLARE @dbs TABLE([name] VARCHAR(100));
|
||||
DECLARE @results TABLE([schema] VARCHAR(100), [table] VARCHAR(100), [db_name] VARCHAR(100));
|
||||
DECLARE @keyword VARCHAR(100) = 'batch';
|
||||
|
||||
DECLARE @query VARCHAR(MAX);
|
||||
DECLARE @tpl VARCHAR(MAX)=' USE @db@;
|
||||
|
||||
SELECT DISTINCT c.TABLE_SCHEMA, c.TABLE_NAME, DB_NAME()
|
||||
FROM INFORMATION_SCHEMA.COLUMNS c
|
||||
WHERE c.TABLE_NAME LIKE ''%@keyword@%'' ';
|
||||
|
||||
INSERT INTO @dbs([name])
|
||||
SELECT db.name
|
||||
FROM sys.databases db
|
||||
WHERE db.database_id > 4;
|
||||
|
||||
/* declare variables */
|
||||
DECLARE @db_name VARCHAR(MAX);
|
||||
|
||||
DECLARE scan_db CURSOR FAST_FORWARD READ_ONLY FOR
|
||||
SELECT [name] FROM @dbs d
|
||||
|
||||
OPEN scan_db
|
||||
|
||||
FETCH NEXT FROM scan_db INTO @db_name
|
||||
|
||||
WHILE @@FETCH_STATUS = 0
|
||||
BEGIN
|
||||
SELECT @query = REPLACE(REPLACE(@tpl,'@keyword@', @keyword), '@db@', @db_name);
|
||||
INSERT INTO @results ([schema], [table], [db_name])
|
||||
EXEC(@query);
|
||||
|
||||
FETCH NEXT FROM scan_db INTO @db_name
|
||||
END
|
||||
|
||||
CLOSE scan_db
|
||||
DEALLOCATE scan_db
|
||||
|
||||
SELECT *
|
||||
FROM @results r;
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
Server: sun791aps.sunstore.ch
|
||||
Format: GCM
|
||||
Business: TPPHAR
|
||||
type: PROD
|
||||
Version: 21.3.11111.00064
|
||||
|
||||
02.11.2022, TSC
|
||||
*/
|
||||
USE [Arizona];
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
SET XACT_ABORT ON;
|
||||
SET NOCOUNT ON;
|
||||
|
||||
DECLARE @index_to_drop TABLE ([columns_in_index] VARCHAR(MAX) NOT NULL,
|
||||
[index_name] VARCHAR(500) NULL);
|
||||
|
||||
INSERT @index_to_drop ([columns_in_index])
|
||||
VALUES ('ET_APS_TS'),
|
||||
('ET_entry_address'),
|
||||
('ET_entry_type'),
|
||||
('ET_master_ID'),
|
||||
('ET_predefined_entry'),
|
||||
('ET_sales_tax_code'),
|
||||
('ET_bmc_user_profile'),
|
||||
('ET_currency');
|
||||
|
||||
/* find index name with defined columns */
|
||||
WITH [ColInfo]
|
||||
AS (SELECT [o].[name] AS [TblName],
|
||||
[s].[name] + '.' + [o].[name] AS [SchemaTbl],
|
||||
[i].[name] AS [IndexName],
|
||||
[i].[is_primary_key] AS [IsPrimaryKey],
|
||||
[i].[is_unique_constraint] AS [IsUniqueConstraint],
|
||||
[i].[is_unique] AS [IsUnique],
|
||||
[c].[name] AS [ColName],
|
||||
[c].[is_computed] AS [IsComputedCol],
|
||||
[ic].[is_included_column] AS [IsIncludedCol],
|
||||
[ic].[key_ordinal],
|
||||
[i].[filter_definition] AS [FilterDefinition],
|
||||
CHAR(13) + CHAR(10) AS [crlf],
|
||||
';' + CHAR(13) + CHAR(10) + 'GO' + CHAR(13) + CHAR(10) AS [crlfgo]
|
||||
FROM [sys].[objects] [o]
|
||||
INNER JOIN [sys].[schemas] [s]
|
||||
ON [o].[schema_id] = [s].[schema_id]
|
||||
INNER JOIN [sys].[columns] [c]
|
||||
ON [o].[object_id] = [c].[object_id]
|
||||
INNER JOIN [sys].[indexes] [i]
|
||||
ON [c].[object_id] = [i].[object_id]
|
||||
INNER JOIN [sys].[index_columns] [ic]
|
||||
ON [i].[index_id] = [ic].[index_id]
|
||||
AND [o].[object_id] = [ic].[object_id]
|
||||
AND [c].[column_id] = [ic].[column_id]
|
||||
WHERE [o].[name] = 'entry'
|
||||
--AND i.filter_definition IS NOT NULL
|
||||
)
|
||||
|
||||
--SELECT itd.columns_in_index
|
||||
-- ,itd.index_name
|
||||
-- ,lst.IndexName
|
||||
-- ,lst.IndexColumns
|
||||
-- ,lst.IncludedColumns
|
||||
UPDATE [itd]
|
||||
SET [itd].[index_name] = [lst].[IndexName]
|
||||
FROM ( SELECT DISTINCT [x].[TblName],
|
||||
[x].[IndexName],
|
||||
[x].[IsPrimaryKey],
|
||||
[x].[IsUniqueConstraint],
|
||||
[x].[IsUnique],
|
||||
--,size.IndexSizeKB
|
||||
[c].[IndexColumns],
|
||||
[ci].[IncludedColumns],
|
||||
[cc].[ComputedColumns],
|
||||
[x].[FilterDefinition],
|
||||
[x].[crlf] + '-- ' + [x].[IndexName] + [x].[crlf] +
|
||||
--check drop
|
||||
'IF INDEXPROPERTY(OBJECT_ID(''' + [x].[SchemaTbl] + '''), ''' + [x].[IndexName]
|
||||
+ ''' , ''IndexID'' ) IS NOT NULL BEGIN;' + [x].[crlf]
|
||||
+ CASE
|
||||
WHEN [x].[IsPrimaryKey] = 1 THEN NULL
|
||||
WHEN [x].[IsUniqueConstraint] = 1 THEN
|
||||
--drop statement
|
||||
' ALTER TABLE ' + [x].[SchemaTbl] + ' DROP CONSTRAINT ' + [x].[IndexName] + ';'
|
||||
+ [x].[crlf] + 'END' + [x].[crlfgo]
|
||||
--check create
|
||||
+ 'IF INDEXPROPERTY(OBJECT_ID(''' + [x].[SchemaTbl] + '''), ''' + [x].[IndexName]
|
||||
+ ''' , ''IndexID'' ) IS NULL BEGIN;' + [x].[crlf] +
|
||||
--create statement
|
||||
+' ALTER TABLE '
|
||||
+ [x].[SchemaTbl] + ' ADD CONSTRAINT ' + [x].[IndexName] + ' UNIQUE ('
|
||||
+ [c].[IndexColumns] + ');' + [x].[crlf] + 'END' + [x].[crlfgo]
|
||||
ELSE
|
||||
--drop statement
|
||||
' DROP INDEX ' + [x].[SchemaTbl] + '.' + [x].[IndexName] + ';' + [x].[crlf]
|
||||
+ 'END' + [x].[crlfgo]
|
||||
--check create
|
||||
+ 'IF INDEXPROPERTY(OBJECT_ID(''' + [x].[SchemaTbl] + '''), ''' + [x].[IndexName]
|
||||
+ ''' , ''IndexID'' ) IS NULL BEGIN;' + [x].[crlf] +
|
||||
--create statement
|
||||
+' CREATE '
|
||||
+ CASE
|
||||
WHEN [x].[IsUnique] = 1 THEN 'UNIQUE '
|
||||
ELSE '' END + 'INDEX ' + [x].[IndexName] + ' ON ' + [x].[SchemaTbl] + '('
|
||||
+ [c].[IndexColumns] + ')'
|
||||
+ ISNULL(' INCLUDE(' + [ci].[IncludedColumns] + ')', '')
|
||||
+ ISNULL(' WHERE ' + [x].[FilterDefinition], '') + ';' + [x].[crlf] + 'END'
|
||||
+ [x].[crlfgo] END AS [DropCreateSQL]
|
||||
FROM [ColInfo] [x]
|
||||
CROSS APPLY ( SELECT STUFF([sq].[strXML], 1, 2, '') AS [IndexColumns]
|
||||
FROM ( SELECT ', ' + ISNULL([x2].[ColName], '')
|
||||
FROM [ColInfo] [x2]
|
||||
WHERE [x].[TblName] = [x2].[TblName]
|
||||
AND [x].[IndexName] = [x2].[IndexName]
|
||||
AND [x2].[IsIncludedCol] = 0
|
||||
ORDER BY [x2].[key_ordinal]
|
||||
FOR XML PATH('')) [sq]([strXML]) ) [c]
|
||||
OUTER APPLY ( SELECT STUFF([sq].[strXML], 1, 2, '') AS [IncludedColumns]
|
||||
FROM ( SELECT ', ' + ISNULL([x2].[ColName], '')
|
||||
FROM [ColInfo] [x2]
|
||||
WHERE [x].[TblName] = [x2].[TblName]
|
||||
AND [x].[IndexName] = [x2].[IndexName]
|
||||
AND [x2].[IsIncludedCol] = 1
|
||||
ORDER BY [x2].[key_ordinal]
|
||||
FOR XML PATH('')) [sq]([strXML]) ) [ci]
|
||||
OUTER APPLY ( SELECT STUFF([sq].[strXML], 1, 2, '') AS [ComputedColumns]
|
||||
FROM ( SELECT ', ' + ISNULL([x2].[ColName], '')
|
||||
FROM [ColInfo] [x2]
|
||||
WHERE [x].[TblName] = [x2].[TblName]
|
||||
AND [x].[IndexName] = [x2].[IndexName]
|
||||
AND [x2].[IsComputedCol] = 1
|
||||
ORDER BY [x2].[key_ordinal]
|
||||
FOR XML PATH('')) [sq]([strXML]) ) [cc] ) [lst]
|
||||
INNER JOIN @index_to_drop [itd]
|
||||
ON [lst].[IndexColumns] = [itd].[columns_in_index];
|
||||
|
||||
SELECT *
|
||||
FROM @index_to_drop [itd];
|
||||
|
||||
/* declare variables */
|
||||
DECLARE @index_name VARCHAR(MAX);
|
||||
|
||||
DECLARE [drop_index] CURSOR LOCAL FAST_FORWARD READ_ONLY FOR
|
||||
SELECT [itd].[index_name]
|
||||
FROM @index_to_drop [itd]
|
||||
WHERE [itd].[index_name] IS NOT NULL;
|
||||
|
||||
OPEN [drop_index];
|
||||
|
||||
FETCH NEXT FROM [drop_index]
|
||||
INTO @index_name;
|
||||
|
||||
WHILE @@FETCH_STATUS = 0
|
||||
BEGIN
|
||||
EXEC ('DROP INDEX ' + @index_name + ' ON dbo.entry;');
|
||||
|
||||
FETCH NEXT FROM [drop_index]
|
||||
INTO @index_name;
|
||||
END;
|
||||
|
||||
CLOSE [drop_index];
|
||||
DEALLOCATE [drop_index];
|
||||
|
||||
|
||||
ROLLBACK TRANSACTION;
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
Server: ssunb008vm01.sunstore.ch
|
||||
Format: GCM
|
||||
Business: TPPHAR
|
||||
type: DEVE
|
||||
Version: 23.1.10016.00066
|
||||
|
||||
01.11.2022, TSC
|
||||
*/
|
||||
USE Arizona;
|
||||
BEGIN TRANSACTION;
|
||||
SET XACT_ABORT ON;
|
||||
SET NOCOUNT ON;
|
||||
|
||||
IF OBJECT_ID('tempdb..#chkRes') IS NOT NULL BEGIN;
|
||||
DROP TABLE #chkRes;
|
||||
END
|
||||
DECLARE @ref_schema VARCHAR(50) = 'dbo';
|
||||
DECLARE @ref_table VARCHAR(100) = 'entry';
|
||||
|
||||
CREATE TABLE #chkRes (
|
||||
tbl_name NVARCHAR(128) NOT NULL
|
||||
,[schema_name] VARCHAR(50) NOT NULL
|
||||
,[type] CHAR(2) NULL
|
||||
,index_name NVARCHAR(128) NULL
|
||||
,[index_id] INT NOT NULL
|
||||
,[Reads] BIGINT NOT NULL
|
||||
,[Writes] BIGINT NOT NULL
|
||||
,[delta] BIGINT NOT NULL
|
||||
,[index_type] NVARCHAR(60) NULL
|
||||
,[FillFactor] TINYINT NULL
|
||||
,[has_filter] BIT NOT NULL
|
||||
,[filter_definition] NVARCHAR(MAX) NULL
|
||||
,[last_user_scan] DATETIME NULL
|
||||
,[last_user_lookup] DATETIME NULL
|
||||
,[last_user_seek] DATETIME NULL
|
||||
,[IndexColumns] NVARCHAR(MAX) NULL
|
||||
,[IncludedColumns] NVARCHAR(MAX) NULL
|
||||
,[primary_table] NVARCHAR(500) NULL
|
||||
,[pk_column_name] NVARCHAR(128) NULL
|
||||
,[fk_constraint_name] NVARCHAR(128) NULL
|
||||
,comp_drop_cmd AS 'DROP INDEX ' + index_name + ' ON ' + [schema_name] + '.' + tbl_name + ';'
|
||||
);
|
||||
|
||||
;WITH ColInfo
|
||||
AS (
|
||||
SELECT
|
||||
o.name AS TblName
|
||||
,s.name + '.' + o.name AS SchemaTbl
|
||||
,i.name AS IndexName
|
||||
,i.is_primary_key AS IsPrimaryKey
|
||||
,i.is_unique_constraint AS IsUniqueConstraint
|
||||
,i.is_unique AS IsUnique
|
||||
,c.name AS ColName
|
||||
,c.is_computed AS IsComputedCol
|
||||
,ic.is_included_column AS IsIncludedCol
|
||||
,ic.key_ordinal
|
||||
,i.filter_definition AS FilterDefinition
|
||||
FROM sys.objects o
|
||||
INNER JOIN sys.schemas s
|
||||
ON o.schema_id = s.schema_id
|
||||
INNER JOIN sys.columns c
|
||||
ON o.object_id = c.object_id
|
||||
INNER JOIN sys.indexes i
|
||||
ON c.object_id = i.object_id
|
||||
INNER JOIN sys.index_columns ic
|
||||
ON i.index_id = ic.index_id
|
||||
AND o.object_id = ic.object_id
|
||||
AND c.column_id = ic.column_id
|
||||
)
|
||||
|
||||
|
||||
--- Index Read/Write stats (all tables in current DB) ordered by Reads (Query 62) (Overall Index Usage - Reads)
|
||||
INSERT INTO #chkRes (
|
||||
tbl_name
|
||||
,[schema_name]
|
||||
,[type]
|
||||
,index_name
|
||||
,index_id
|
||||
,Reads
|
||||
,Writes
|
||||
,delta
|
||||
,index_type
|
||||
,[FillFactor]
|
||||
,has_filter
|
||||
,filter_definition
|
||||
,last_user_scan
|
||||
,last_user_lookup
|
||||
,last_user_seek
|
||||
,IndexColumns
|
||||
,IncludedColumns
|
||||
,primary_table
|
||||
,pk_column_name
|
||||
,fk_constraint_name
|
||||
)
|
||||
SELECT
|
||||
o.name AS [tbl_Name]
|
||||
,SCHEMA_NAME(o.schema_id) AS [schema_name]
|
||||
,o.type
|
||||
,i.name AS [index_name]
|
||||
,i.index_id
|
||||
,user_seeks + user_scans + user_lookups AS [Reads]
|
||||
,s.user_updates AS [Writes]
|
||||
,CASE
|
||||
WHEN s.user_updates > user_seeks + user_scans + user_lookups THEN
|
||||
s.user_updates - (user_seeks + user_scans + user_lookups)
|
||||
ELSE (user_seeks + user_scans + user_lookups) - s.user_updates
|
||||
END AS delta
|
||||
,i.type_desc AS [index_type]
|
||||
,i.fill_factor AS [fill_factor]
|
||||
,i.has_filter
|
||||
,i.filter_definition
|
||||
,s.last_user_scan
|
||||
,s.last_user_lookup
|
||||
,s.last_user_seek
|
||||
,idx_cols.IndexColumns
|
||||
,idx_cols.IncludedColumns
|
||||
,fk.primary_table
|
||||
,fk.pk_column_name
|
||||
,fk.fk_constraint_name
|
||||
FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
|
||||
INNER JOIN sys.indexes AS i WITH (NOLOCK)
|
||||
ON s.object_id = i.object_id
|
||||
INNER JOIN sys.objects o WITH (NOLOCK)
|
||||
ON s.object_id = o.object_id
|
||||
OUTER APPLY (
|
||||
SELECT DISTINCT
|
||||
x.TblName
|
||||
,x.IndexName
|
||||
,x.IsPrimaryKey
|
||||
,x.IsUniqueConstraint
|
||||
,x.IsUnique
|
||||
--,size.IndexSizeKB
|
||||
,c.IndexColumns
|
||||
,ci.IncludedColumns
|
||||
,cc.ComputedColumns
|
||||
,x.FilterDefinition
|
||||
FROM ColInfo x
|
||||
CROSS APPLY (
|
||||
SELECT STUFF(sq.strXML, 1, 2, '') AS IndexColumns
|
||||
FROM (
|
||||
SELECT ', ' + ISNULL(x2.ColName, '')
|
||||
FROM ColInfo x2
|
||||
WHERE x.TblName = x2.TblName
|
||||
AND x.IndexName = x2.IndexName
|
||||
AND x2.IsIncludedCol = 0
|
||||
ORDER BY x2.key_ordinal
|
||||
FOR XML PATH('')
|
||||
) sq(strXML)
|
||||
) c
|
||||
OUTER APPLY (
|
||||
SELECT STUFF(sq.strXML, 1, 2, '') AS IncludedColumns
|
||||
FROM (
|
||||
SELECT ', ' + ISNULL(x2.ColName, '')
|
||||
FROM ColInfo x2
|
||||
WHERE x.TblName = x2.TblName
|
||||
AND x.IndexName = x2.IndexName
|
||||
AND x2.IsIncludedCol = 1
|
||||
ORDER BY x2.key_ordinal
|
||||
FOR XML PATH('')
|
||||
) sq(strXML)
|
||||
) ci
|
||||
OUTER APPLY (
|
||||
SELECT STUFF(sq.strXML, 1, 2, '') AS ComputedColumns
|
||||
FROM (
|
||||
SELECT ', ' + ISNULL(x2.ColName, '')
|
||||
FROM ColInfo x2
|
||||
WHERE x.TblName = x2.TblName
|
||||
AND x.IndexName = x2.IndexName
|
||||
AND x2.IsComputedCol = 1
|
||||
ORDER BY x2.key_ordinal
|
||||
FOR XML PATH('')
|
||||
) sq(strXML)
|
||||
) cc
|
||||
WHERE x.TblName = o.name
|
||||
AND x.IndexName = i.name
|
||||
) idx_cols
|
||||
LEFT OUTER JOIN (
|
||||
SELECT
|
||||
SCHEMA_NAME(tab.schema_id) + '.' + tab.name AS [table]
|
||||
,col.column_id
|
||||
,col.name AS column_name
|
||||
,CASE
|
||||
WHEN fk.object_id IS NOT NULL THEN '>-'
|
||||
ELSE NULL
|
||||
END AS rel
|
||||
,SCHEMA_NAME(pk_tab.schema_id) + '.' + pk_tab.name AS primary_table
|
||||
,pk_col.name AS pk_column_name
|
||||
,fk_cols.constraint_column_id AS NO
|
||||
,fk.name AS fk_constraint_name
|
||||
,tab.name AS tbl_name
|
||||
FROM sys.tables tab
|
||||
INNER JOIN sys.columns col
|
||||
ON col.object_id = tab.object_id
|
||||
LEFT OUTER JOIN sys.foreign_key_columns fk_cols
|
||||
ON fk_cols.parent_object_id = tab.object_id
|
||||
AND fk_cols.parent_column_id = col.column_id
|
||||
LEFT OUTER JOIN sys.foreign_keys fk
|
||||
ON fk.object_id = fk_cols.constraint_object_id
|
||||
LEFT OUTER JOIN sys.tables pk_tab
|
||||
ON pk_tab.object_id = fk_cols.referenced_object_id
|
||||
LEFT OUTER JOIN sys.columns pk_col
|
||||
ON pk_col.column_id = fk_cols.referenced_column_id
|
||||
AND pk_col.object_id = fk_cols.referenced_object_id
|
||||
WHERE fk.object_id IS NOT NULL
|
||||
) fk
|
||||
ON fk.tbl_name = o.name
|
||||
AND idx_cols.IndexColumns LIKE '%' + fk.column_name + '%'
|
||||
WHERE o.type = 'U' -- user table
|
||||
AND i.index_id = s.index_id
|
||||
AND s.database_id = DB_ID()
|
||||
AND o.name = @ref_table
|
||||
AND SCHEMA_NAME(o.schema_id) = @ref_schema
|
||||
ORDER BY delta DESC
|
||||
--ORDER BY user_seeks + user_scans + user_lookups DESC -- Order by reads
|
||||
--ORDER BY s.user_updates DESC OPTION -- Order by writes
|
||||
OPTION (RECOMPILE);
|
||||
|
||||
|
||||
|
||||
/* list back data */
|
||||
SELECT cr.tbl_name
|
||||
,cr.[schema_name]
|
||||
,cr.[type]
|
||||
,cr.index_name
|
||||
,cr.index_id
|
||||
,cr.Reads
|
||||
,cr.Writes
|
||||
,cr.delta
|
||||
,cr.index_type
|
||||
,cr.[FillFactor]
|
||||
,cr.has_filter
|
||||
,cr.filter_definition
|
||||
,cr.last_user_scan
|
||||
,cr.last_user_lookup
|
||||
,cr.last_user_seek
|
||||
,cr.IndexColumns
|
||||
,cr.IncludedColumns
|
||||
,cr.primary_table
|
||||
,cr.pk_column_name
|
||||
,cr.fk_constraint_name
|
||||
FROM #chkRes cr
|
||||
ORDER BY IndexColumns
|
||||
;
|
||||
|
||||
ROLLBACK TRANSACTION
|
||||
|
||||
RETURN
|
||||
|
||||
|
||||
--/* Check if index with FK are in the list, alert if it's the case */
|
||||
--IF EXISTS(
|
||||
-- SELECT 1
|
||||
-- FROM #chkRes cr
|
||||
-- WHERE cr.Reads = 0 --not used
|
||||
-- AND cr.Writes > 100 --but maintained
|
||||
-- AND cr.fk_constraint_name IS NOT NULL --is part of a FK
|
||||
--) BEGIN
|
||||
-- SELECT
|
||||
-- 'This index is tied to a column part of a FK, check if dropping is to be done.' AS msg
|
||||
-- ,cr.[schema_name]
|
||||
-- ,cr.tbl_name
|
||||
-- ,cr.index_name
|
||||
-- ,cr.Reads
|
||||
-- ,cr.Writes
|
||||
-- ,cr.primary_table
|
||||
-- ,cr.pk_column_name
|
||||
-- ,cr.fk_constraint_name
|
||||
-- FROM #chkRes cr
|
||||
-- WHERE cr.Reads = 0 --not used
|
||||
-- AND cr.Writes > 100 --but maintained
|
||||
-- AND cr.fk_constraint_name IS NOT NULL --is part of a FK
|
||||
-- AND cr.index_type = 'NONCLUSTERED'
|
||||
--END
|
||||
|
||||
|
||||
--/* declare variables */
|
||||
--DECLARE
|
||||
-- @table_name VARCHAR(MAX)
|
||||
-- ,@schema_name VARCHAR(20)
|
||||
-- ,@index_name VARCHAR(500)
|
||||
-- ,@drop_cmd VARCHAR(MAX)
|
||||
-- ;
|
||||
|
||||
--DECLARE dropIdx CURSOR LOCAL FORWARD_ONLY FAST_FORWARD READ_ONLY FOR
|
||||
-- SELECT
|
||||
-- cr.tbl_name
|
||||
-- ,cr.[schema_name]
|
||||
-- ,cr.index_name
|
||||
-- ,cr.comp_drop_cmd
|
||||
-- FROM #chkRes cr
|
||||
-- WHERE cr.Reads = 0 --not used on select
|
||||
-- AND cr.Writes > 100 --but maintained on insert / update
|
||||
-- AND cr.fk_constraint_name IS NULL --not part of a FK
|
||||
-- AND cr.index_type = 'NONCLUSTERED' --ignore clustered & heaps
|
||||
-- ;
|
||||
|
||||
--OPEN dropIdx;
|
||||
|
||||
--FETCH NEXT FROM dropIdx
|
||||
--INTO
|
||||
-- @table_name
|
||||
-- ,@schema_name
|
||||
-- ,@index_name
|
||||
-- ,@drop_cmd
|
||||
-- ;
|
||||
|
||||
--WHILE @@FETCH_STATUS = 0 BEGIN
|
||||
-- PRINT @drop_cmd;--'Dropping index ' + @index_name + ' on ' + @schema_name + '.' + @table_name;
|
||||
-- --EXEC(@drop_cmd);
|
||||
-- FETCH NEXT FROM dropIdx
|
||||
-- INTO
|
||||
-- @table_name
|
||||
-- ,@schema_name
|
||||
-- ,@index_name
|
||||
-- ,@drop_cmd
|
||||
-- ;
|
||||
--END
|
||||
|
||||
--CLOSE dropIdx;
|
||||
--DEALLOCATE dropIdx;
|
||||
|
||||
|
||||
--SELECT cr.tbl_name
|
||||
-- ,cr.[schema_name]
|
||||
-- ,cr.index_name
|
||||
-- ,cr.Reads
|
||||
-- ,cr.Writes
|
||||
-- ,cr.delta
|
||||
-- ,cr.index_type
|
||||
-- ,cr.last_user_scan
|
||||
-- ,cr.last_user_lookup
|
||||
-- ,cr.last_user_seek
|
||||
-- ,cr.IndexColumns
|
||||
-- ,cr.IncludedColumns
|
||||
-- ,cr.primary_table
|
||||
-- ,cr.pk_column_name
|
||||
-- ,cr.fk_constraint_name
|
||||
--FROM #chkRes cr
|
||||
--ORDER BY cr.delta DESC
|
||||
--;
|
||||
|
||||
--ROLLBACK TRANSACTION;
|
||||
@@ -0,0 +1,686 @@
|
||||
/*=============================================================================
|
||||
|
||||
Maintain and align indexes on Arizona.dbo.entry
|
||||
|
||||
TODO:
|
||||
allow to skip INDEX DROP for unlisted indexes
|
||||
|
||||
Parameters
|
||||
----------------------
|
||||
Populate the var table @indexesToMaintains to drop / create and rename indexes to be aligned.
|
||||
You need to specify those fields:
|
||||
* schemaName, tableName: the schema and table name for which you want to maintain the indexes.
|
||||
This script was written with working against several tables in mind, but is not (yet) tested to handle this case.
|
||||
* columnsName: What columns are part of the index key, in the correct order
|
||||
As we have divergent indexes names, we must use the index columns as the key to find the indexes.
|
||||
* includedColumns_inp: if the index have included columns, list them here.
|
||||
When an index is found, the included columns list it also compared to determine if the index is aligned.
|
||||
* expectedIndexName_inp: The name the index should have. It will be used when creating missing indexes or renaming existing indexes
|
||||
The index name is optionnal if we specify in the table that the index must be dropped.
|
||||
* isToBeDeleted_inp: if true, indicate that the index should be dropped.
|
||||
* isToBeRecreated_inp: if true, will drop and re-create the index even if the structure matches.
|
||||
* isClustered_inp: if true and the index must be (re-)created, it will be created as a clustered index
|
||||
* isUnique_inp: if true and the index must be (re-)created, it will be created as a unique index
|
||||
|
||||
Note that:
|
||||
* Matching is always made from the columns of the index, never on the name of the index.
|
||||
The reason is that we might have difference in the name of the index between several servers.
|
||||
* If an index exists with the name A, but your list contains a similar index with the name B, index A will be renamed to B with sp_rename
|
||||
* For each table(s) referenced in @indexesToMaintains, if an index exists that is not listed in the variable table it will be considered to be
|
||||
dropped if @DoDropExistingIndexesNotReferenced is set to 1.
|
||||
* If you list a specific existing index to be dropped in @indexesToMaintains then the following fields are optionnal:
|
||||
expected index name
|
||||
flag isClustered
|
||||
flag isUnique
|
||||
|
||||
Context
|
||||
----------------------
|
||||
In any databases
|
||||
|
||||
Creation : 09.11.2022 / TSC
|
||||
Modifications:
|
||||
|
||||
=============================================================================*/
|
||||
USE [Arizona];
|
||||
GO
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
SET XACT_ABORT ON;
|
||||
SET NOCOUNT ON;
|
||||
|
||||
DECLARE @query VARCHAR(MAX); /* for dynamic sql */
|
||||
DECLARE @schemaName VARCHAR(100); /* used in cursor */
|
||||
DECLARE @tableName VARCHAR(100); /* used in cursor */
|
||||
|
||||
/*
|
||||
If set to true, the script will not apply the change.
|
||||
Generated statements are always printed on the message tab.
|
||||
*/
|
||||
DECLARE @DoOnlyOutputStatement BIT = 0;
|
||||
|
||||
/*
|
||||
If the script find local indexes that are not listed in the variable table, should it drop them or not ?
|
||||
An index whose definition is not aligned with the temp table or flagged "to be rebuilt" will always be dropped before creation.
|
||||
*/
|
||||
DECLARE @DoDropExistingIndexesNotReferenced BIT = 0;
|
||||
|
||||
DECLARE @indexesToMaintains TABLE(
|
||||
[Id] INT IDENTITY NOT NULL
|
||||
,[SchemaName] VARCHAR(100) NOT NULL /* the schema the table is part of */
|
||||
,[TableName] VARCHAR(100) NOT NULL /* On which table is the index, whithout the schema */
|
||||
,columnsName_inp VARCHAR(MAX) NOT NULL /* list all the columns of the index, in the correct orders */
|
||||
,isClustered_inp BIT NOT NULL DEFAULT 0 /* Is the indexe a clustered index ? */
|
||||
,isUnique_inp BIT NOT NULL DEFAULT 0 /* Is the index unique ? */
|
||||
,includedColumns_inp VARCHAR(MAX)NULL /* list all the included columns the index should have. only relevant for idx not to be dropped */
|
||||
,isToBeDeleted_inp BIT NOT NULL DEFAULT 0 /* must the script drop this index ? */
|
||||
,isToBeRecreated_inp BIT NOT NULL DEFAULT 0 /* must the script recreate this index ?
|
||||
If an existing index differs from the definition, it will be dropped and the correct one created.
|
||||
Use this only to force the re-creation even if the structure is identical */
|
||||
,expectedIndexName_inp VARCHAR(MAX) NULL /* the name the index should have, used to determine if a rename of the existing index should be done */
|
||||
,indexName VARCHAR(MAX) NULL /* computed by the script. The index name on this system, as indexes name can be different accross systems*/
|
||||
,isIndexFound_comp BIT NOT NULL DEFAULT 0 /* computed by the script. set to 1 if the index is present on the server */
|
||||
,isToBeCreated_comp BIT NULL DEFAULT 0 /* computed by the script. does this index will be created on the server ? */
|
||||
,isToBeRenamed_comp BIT NULL DEFAULT 0 /* computed by the script. does this index will be renamed ? */
|
||||
,isToBeDeleted_comp BIT NULL DEFAULT 0 /* computed by the script. does this index will be deleted ? */
|
||||
,isToBeRecreated_comp BIT NULL DEFAULT 0 /* computed by the script. does this index will be re-created ? */
|
||||
,internal_includedColumnsMatch BIT NULL /* computed ba the script. Are the expected and actual included columns identical ? */
|
||||
,internal_actualColumns VARCHAR(MAX) NULL /* the actual columns in the existing index */
|
||||
,internal_actualIncludedColumns VARCHAR(MAX) NULL /* the actual columns in the INCLUDE part of the index */
|
||||
,internal_statusMsg VARCHAR(MAX) /* Message, used for debug */
|
||||
);
|
||||
|
||||
/* Declare the expected structure of the indexes */
|
||||
INSERT INTO @indexesToMaintains ([columnsName_inp],
|
||||
[isToBeDeleted_inp],
|
||||
[isToBeRecreated_inp],
|
||||
[expectedIndexName_inp],
|
||||
[includedColumns_inp],
|
||||
[schemaName],
|
||||
[tableName],
|
||||
[isClustered_inp],
|
||||
[isUnique_inp])
|
||||
/* indexe(s) we do want */
|
||||
VALUES('Entry_id',0,0,'PK_Entry_id', NULL,'dbo','entry',0,0)
|
||||
,('ET_batch_run',0,0,'NCIX_Entry_COL_ET_batch_run',NULL,'dbo','entry',0,0)
|
||||
,('ET_accounting_period',0,0,'NCIX_Entry_COL_ET_accounting_period','Entry_ID','dbo','entry',0,0)
|
||||
,('ET_document_header',0,0,'NCIX_Entry_COL_ET_document_header','ET_debit_currency_amount, ET_credit_currency_amount','dbo','entry',0,0)
|
||||
,('ET_account, ET_document_header',0,0,'NCIX_Entry_COL_ET_account','Entry_ID','dbo','entry',0,0)
|
||||
,('ET_reconciliation_status, ET_account, ET_document_header',0,0,'NCIX_Entry_COL_ET_reconciliation_status','ET_debit_base_amount, ET_credit_base_amount, ET_reconciliation_base_amount','dbo','entry',0,0)
|
||||
,('ET_predefined_entry',0,0,'NCIX_Entry_COL_ET_predefined_entry',NULL,'dbo','entry',0,0)
|
||||
,('ET_entry_address',0,0,'NCIX_Entry_COL_ET_entry_address',NULL,'dbo','entry',0,0)
|
||||
,('ET_currency',0,0,'NCIX_Entry_COL_ET_currency',NULL,'dbo','entry',0,0)
|
||||
|
||||
/* indexe(s) used in central, even if not in pharmacies*/
|
||||
,('ET_entry_type',0,0,'NCIX_Entry_COL_ET_entry_type',NULL,'dbo','entry',0,0)
|
||||
|
||||
/* indexe(s) we really want to drop*/
|
||||
,('ET_master_ID',1,0,'NCIX_Entry_COL_ET_master_ID',NULL,'dbo','entry',0,0)
|
||||
,('ET_bmc_user_profile',1,0,'NCIX_Entry_COL_ET_bmc_user_profile',NULL,'dbo','entry',0,0)
|
||||
,('ET_sales_tax_code',1,0,'NCIX_Entry_COL_ET_sales_tax_code',NULL,'dbo','entry',0,0)
|
||||
,('ET_APS_TS', 1, 0, 'NCIX_Entry_COL_ET_APS_TS',NULL,'dbo','entry',0,0)
|
||||
|
||||
;
|
||||
|
||||
/* check for duplicate */
|
||||
DECLARE @msg VARCHAR(MAX) = '';
|
||||
SELECT @msg = @msg + 'Duplicate index (on expected index name) found in indexes definition. Table ['+[itm].[SchemaName]+'].['+[itm].[TableName]+'], expected index name ['+[itm].[expectedIndexName_inp]+']'+CHAR(13)+CHAR(10)
|
||||
FROM @indexesToMaintains [itm]
|
||||
JOIN (
|
||||
SELECT
|
||||
[ii].[expectedIndexName_inp] AS [key]
|
||||
FROM @indexesToMaintains [ii]
|
||||
GROUP BY [ii].[expectedIndexName_inp]
|
||||
HAVING COUNT(1)>1
|
||||
) d ON d.[key] = [itm].[expectedIndexName_inp]
|
||||
GROUP BY [itm].[SchemaName], [itm].[TableName], [itm].[expectedIndexName_inp]
|
||||
;
|
||||
|
||||
/* stop here if we have an issue in the variable table */
|
||||
IF @msg <> ''
|
||||
BEGIN
|
||||
RAISERROR(@msg, 16, 4);
|
||||
IF @@TRANCOUNT>0
|
||||
BEGIN
|
||||
ROLLBACK TRANSACTION;
|
||||
END
|
||||
RETURN
|
||||
END
|
||||
|
||||
SET @msg = '';
|
||||
SELECT @msg = @msg + 'Duplicate index (on column definition) found in indexes definition. Table ['+[itm].[SchemaName]+'].['+[itm].[TableName]+'], columns in index: '+[itm].[columnsName_inp]
|
||||
+ CASE
|
||||
WHEN [itm].[includedColumns_inp] IS NOT NULL THEN ', included colum(s): '+[itm].[includedColumns_inp]
|
||||
ELSE ''
|
||||
END
|
||||
+CHAR(13)+CHAR(10)
|
||||
FROM @indexesToMaintains [itm]
|
||||
JOIN (
|
||||
SELECT
|
||||
[ii].[columnsName_inp]+'_'+ISNULL([ii].[includedColumns_inp],'') AS [key]
|
||||
FROM @indexesToMaintains [ii]
|
||||
GROUP BY [ii].[columnsName_inp]+'_'+ISNULL([ii].[includedColumns_inp],'')
|
||||
HAVING COUNT(1)>1
|
||||
) d ON d.[key] = [itm].[columnsName_inp]+'_'+ISNULL([itm].[includedColumns_inp],'')
|
||||
GROUP BY [itm].[SchemaName], [itm].[TableName], [itm].[columnsName_inp], [itm].[includedColumns_inp]
|
||||
;
|
||||
|
||||
/* stop here if we have an issue in the variable table */
|
||||
IF @msg <> ''
|
||||
BEGIN
|
||||
RAISERROR(@msg, 16, 4);
|
||||
IF @@TRANCOUNT>0
|
||||
BEGIN
|
||||
ROLLBACK TRANSACTION;
|
||||
END
|
||||
RETURN
|
||||
END
|
||||
|
||||
/* fetch the indexes name on the current instance */
|
||||
DECLARE curIdxName CURSOR FAST_FORWARD READ_ONLY FOR
|
||||
SELECT DISTINCT [itm].[schemaName], [itm].[tableName]
|
||||
FROM @indexesToMaintains [itm];
|
||||
|
||||
OPEN curIdxName;
|
||||
|
||||
FETCH NEXT FROM curIdxName INTO @schemaName, @tableName;
|
||||
|
||||
WHILE @@FETCH_STATUS = 0
|
||||
BEGIN
|
||||
|
||||
;WITH ColInfo
|
||||
AS (
|
||||
SELECT
|
||||
o.[Name] AS TblName
|
||||
,s.[Name] + '.' + o.[Name] AS SchemaTbl
|
||||
,i.[Name] AS IndexName
|
||||
,i.[is_primary_key] AS IsPrimaryKey
|
||||
,i.[is_unique_constraint] AS IsUniqueConstraint
|
||||
,i.[is_unique] AS IsUnique
|
||||
,c.[Name] AS ColName
|
||||
,c.[is_computed] AS IsComputedCol
|
||||
,ic.[is_included_column] AS IsIncludedCol
|
||||
,ic.[key_ordinal]
|
||||
,i.[filter_definition] AS FilterDefinition
|
||||
FROM [sys].[objects] o
|
||||
INNER JOIN [sys].[schemas] s
|
||||
ON o.SCHEMA_ID = s.SCHEMA_ID
|
||||
INNER JOIN [sys].[COLUMNS] c
|
||||
ON o.OBJECT_ID = c.OBJECT_ID
|
||||
INNER JOIN [sys].[indexes] i
|
||||
ON c.OBJECT_ID = i.OBJECT_ID
|
||||
INNER JOIN [sys].[index_columns] ic
|
||||
ON i.[index_id] = ic.[index_id]
|
||||
AND o.OBJECT_ID = ic.OBJECT_ID
|
||||
AND c.[column_id] = ic.[column_id]
|
||||
)
|
||||
|
||||
/* map the list to the actual indexes */
|
||||
UPDATE [itm] SET [itm].[indexName] = i.[name]
|
||||
,[itm].[internal_includedColumnsMatch] = CASE
|
||||
WHEN ISNULL([itm].[includedColumns_inp],'') = ISNULL([idx_cols].[includedColumns],'') THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
,[itm].[internal_actualIncludedColumns] = [idx_cols].[includedColumns]
|
||||
,[itm].[internal_actualColumns] = [idx_cols].[IndexColumns]
|
||||
,[itm].[isIndexFound_comp] = 1
|
||||
,[itm].[isToBeCreated_comp] = 0
|
||||
FROM [sys].[indexes] AS i WITH (NOLOCK)
|
||||
INNER JOIN [sys].[objects] o WITH (NOLOCK)
|
||||
ON i.OBJECT_ID = o.OBJECT_ID
|
||||
OUTER APPLY (
|
||||
SELECT DISTINCT
|
||||
x.TblName
|
||||
,x.IndexName
|
||||
,x.IsPrimaryKey
|
||||
,x.IsUniqueConstraint
|
||||
,x.IsUnique
|
||||
,c.IndexColumns
|
||||
,ci.includedColumns
|
||||
,cc.ComputedColumns
|
||||
,x.FilterDefinition
|
||||
FROM ColInfo x
|
||||
CROSS APPLY (
|
||||
SELECT STUFF(sq.strXML, 1, 2, '') AS IndexColumns
|
||||
FROM (
|
||||
SELECT ', ' + ISNULL(x2.ColName, '')
|
||||
FROM ColInfo x2
|
||||
WHERE x.TblName = x2.TblName
|
||||
AND x.IndexName = x2.IndexName
|
||||
AND x2.IsIncludedCol = 0
|
||||
ORDER BY x2.[key_ordinal]
|
||||
FOR XML PATH('')
|
||||
) sq(strXML)
|
||||
) c
|
||||
OUTER APPLY (
|
||||
SELECT STUFF(sq.strXML, 1, 2, '') AS includedColumns
|
||||
FROM (
|
||||
SELECT ', ' + ISNULL(x2.ColName, '')
|
||||
FROM ColInfo x2
|
||||
WHERE x.TblName = x2.TblName
|
||||
AND x.IndexName = x2.IndexName
|
||||
AND x2.IsIncludedCol = 1
|
||||
ORDER BY x2.[key_ordinal]
|
||||
FOR XML PATH('')
|
||||
) sq(strXML)
|
||||
) ci
|
||||
OUTER APPLY (
|
||||
SELECT STUFF(sq.strXML, 1, 2, '') AS ComputedColumns
|
||||
FROM (
|
||||
SELECT ', ' + ISNULL(x2.ColName, '')
|
||||
FROM ColInfo x2
|
||||
WHERE x.TblName = x2.TblName
|
||||
AND x.IndexName = x2.IndexName
|
||||
AND x2.IsComputedCol = 1
|
||||
ORDER BY x2.[key_ordinal]
|
||||
FOR XML PATH('')
|
||||
) sq(strXML)
|
||||
) cc
|
||||
WHERE x.TblName = o.[Name]
|
||||
AND x.IndexName = i.[Name]
|
||||
) idx_cols
|
||||
LEFT OUTER JOIN (
|
||||
SELECT
|
||||
SCHEMA_NAME(tab.SCHEMA_ID) + '.' + tab.[Name] AS [table]
|
||||
,col.[column_id]
|
||||
,col.[Name] AS [COLUMN_NAME]
|
||||
,CASE
|
||||
WHEN fk.OBJECT_ID IS NOT NULL THEN '>-'
|
||||
ELSE NULL
|
||||
END AS rel
|
||||
,SCHEMA_NAME(pk_tab.SCHEMA_ID) + '.' + pk_tab.[Name] AS primary_table
|
||||
,pk_col.[Name] AS pk_column_name
|
||||
,fk_cols.[constraint_column_id] AS NO
|
||||
,fk.[Name] AS fk_constraint_name
|
||||
,tab.[Name] AS tbl_name
|
||||
FROM [sys].[TABLES] tab
|
||||
INNER JOIN [sys].[COLUMNS] col
|
||||
ON col.OBJECT_ID = tab.OBJECT_ID
|
||||
LEFT OUTER JOIN [sys].[foreign_key_columns] fk_cols
|
||||
ON fk_cols.[parent_object_id] = tab.OBJECT_ID
|
||||
AND fk_cols.[parent_column_id] = col.[column_id]
|
||||
LEFT OUTER JOIN [sys].[foreign_keys] fk
|
||||
ON fk.OBJECT_ID = fk_cols.[constraint_object_id]
|
||||
LEFT OUTER JOIN [sys].[TABLES] pk_tab
|
||||
ON pk_tab.OBJECT_ID = fk_cols.[referenced_object_id]
|
||||
LEFT OUTER JOIN [sys].[COLUMNS] pk_col
|
||||
ON pk_col.[column_id] = fk_cols.[referenced_column_id]
|
||||
AND pk_col.OBJECT_ID = fk_cols.[referenced_object_id]
|
||||
WHERE fk.OBJECT_ID IS NOT NULL
|
||||
) fk
|
||||
ON fk.tbl_name = o.[Name]
|
||||
AND idx_cols.IndexColumns LIKE '%' + fk.[COLUMN_NAME] + '%'
|
||||
INNER JOIN @indexesToMaintains [itm]
|
||||
ON itm.[schemaName] = @schemaName
|
||||
AND itm.[tableName] = @tableName
|
||||
AND itm.[columnsName_inp] COLLATE Latin1_General_CI_AI = [idx_cols].[IndexColumns] COLLATE Latin1_General_CI_AI
|
||||
AND ISNULL([itm].[includedColumns_inp],'') COLLATE Latin1_General_CI_AI = ISNULL([idx_cols].[includedColumns],'') COLLATE Latin1_General_CI_AI
|
||||
WHERE o.TYPE = 'U' -- user table
|
||||
--AND i.[index_id] = s.[index_id]
|
||||
--AND s.[database_id] = DB_ID()
|
||||
AND o.[Name] = @tableName
|
||||
AND SCHEMA_NAME(o.SCHEMA_ID) = @schemaName
|
||||
OPTION (RECOMPILE);
|
||||
|
||||
|
||||
/* Add the indexes not in the list but present on the server to the list */
|
||||
;WITH ColInfo
|
||||
AS (
|
||||
SELECT
|
||||
o.[Name] AS TblName
|
||||
,SCHEMA_NAME(o.[schema_id]) AS SchemaName
|
||||
,s.[Name] + '.' + o.[Name] AS SchemaTbl
|
||||
,i.[Name] AS IndexName
|
||||
,i.[is_primary_key] AS IsPrimaryKey
|
||||
,i.[is_unique_constraint] AS IsUniqueConstraint
|
||||
,i.[is_unique] AS IsUnique
|
||||
,c.[Name] AS ColName
|
||||
,c.[is_computed] AS IsComputedCol
|
||||
,ic.[is_included_column] AS IsIncludedCol
|
||||
,ic.[key_ordinal]
|
||||
,i.[filter_definition] AS FilterDefinition
|
||||
FROM [sys].[objects] o
|
||||
INNER JOIN [sys].[schemas] s
|
||||
ON o.SCHEMA_ID = s.SCHEMA_ID
|
||||
INNER JOIN [sys].[COLUMNS] c
|
||||
ON o.OBJECT_ID = c.OBJECT_ID
|
||||
INNER JOIN [sys].[indexes] i
|
||||
ON c.OBJECT_ID = i.OBJECT_ID
|
||||
INNER JOIN [sys].[index_columns] ic
|
||||
ON i.[index_id] = ic.[index_id]
|
||||
AND o.OBJECT_ID = ic.OBJECT_ID
|
||||
AND c.[column_id] = ic.[column_id]
|
||||
)
|
||||
|
||||
INSERT INTO @indexesToMaintains (
|
||||
[schemaName]
|
||||
,[tableName]
|
||||
,[columnsName_inp]
|
||||
,[indexName]
|
||||
,[isToBeDeleted_inp]
|
||||
,[isToBeDeleted_comp]
|
||||
,[isToBeRecreated_inp]
|
||||
,[internal_statusMsg]
|
||||
,[internal_actualIncludedColumns]
|
||||
,[internal_actualColumns]
|
||||
,[isIndexFound_comp]
|
||||
,[isToBeCreated_comp]
|
||||
,[expectedIndexName_inp]
|
||||
)
|
||||
SELECT DISTINCT
|
||||
@schemaName AS [SchemaName]
|
||||
,@tableName AS [TableName]
|
||||
,c.[IndexColumns] AS [columnsName]
|
||||
,x.[IndexName] AS indexName
|
||||
,0 AS [isToBeDeleted_inp]
|
||||
,CASE
|
||||
WHEN EXISTS(
|
||||
SELECT 1
|
||||
FROM @indexesToMaintains [itm]
|
||||
WHERE itm.[expectedIndexName_inp] = x.[IndexName]
|
||||
OR ISNULL(itm.[columnsName_inp],'') <> ISNULL(itm.[internal_actualColumns],'')
|
||||
OR ISNULL(itm.[includedColumns_inp],'') <> ISNULL(itm.[internal_actualIncludedColumns],'')
|
||||
) THEN 1
|
||||
WHEN @DoDropExistingIndexesNotReferenced = 1 THEN 1
|
||||
ELSE 0
|
||||
END AS [isToBeDeleted_comp]
|
||||
,0 AS [isToBeRecreated_inp]
|
||||
,CASE
|
||||
WHEN EXISTS(
|
||||
SELECT 1
|
||||
FROM @indexesToMaintains [itm]
|
||||
WHERE itm.[expectedIndexName_inp] = x.[IndexName]
|
||||
OR ISNULL(itm.[columnsName_inp],'') <> ISNULL(itm.[internal_actualColumns],'')
|
||||
OR ISNULL(itm.[includedColumns_inp],'') <> ISNULL(itm.[internal_actualIncludedColumns],'')
|
||||
) THEN 'Index present locally not as expected, drop before creation'
|
||||
WHEN @DoDropExistingIndexesNotReferenced = 1 THEN 'Index present locally only'
|
||||
ELSE NULL
|
||||
END AS [internal_statusMsg]
|
||||
,ci.[includedColumns] AS [internal_actualIncludedColumns]
|
||||
,c.[IndexColumns] AS [internal_actualColumns]
|
||||
,1 AS [isIndexFound_comp]
|
||||
,0 AS [isToBeCreated_comp]
|
||||
,x.[IndexName] AS [expectedIndexName_inp]
|
||||
FROM ColInfo x
|
||||
CROSS APPLY(SELECT IndexColumns = STUFF(sq.strXML, 1, 2, '')
|
||||
FROM ( SELECT ', ' + ISNULL(x2.ColName, '')
|
||||
FROM ColInfo x2
|
||||
WHERE x.TblName = x2.TblName AND x.IndexName = x2.IndexName AND x2.IsIncludedCol = 0
|
||||
ORDER BY x2.key_ordinal
|
||||
FOR XML PATH('')
|
||||
) sq (strXML)
|
||||
) c
|
||||
Outer APPLY(SELECT IncludedColumns = STUFF(sq.strXML, 1, 2, '')
|
||||
FROM ( SELECT ', ' + ISNULL(x2.ColName, '')
|
||||
FROM ColInfo x2
|
||||
WHERE x.TblName = x2.TblName AND x.IndexName = x2.IndexName AND x2.IsIncludedCol = 1
|
||||
ORDER BY x2.key_ordinal
|
||||
FOR XML PATH('')
|
||||
) sq (strXML)
|
||||
) ci
|
||||
Outer APPLY(SELECT ComputedColumns = STUFF(sq.strXML, 1, 2, '')
|
||||
FROM ( SELECT ', ' + ISNULL(x2.ColName, '')
|
||||
FROM ColInfo x2
|
||||
WHERE x.TblName = x2.TblName AND x.IndexName = x2.IndexName AND x2.IsComputedCol = 1
|
||||
ORDER BY x2.key_ordinal
|
||||
FOR XML PATH('')
|
||||
) sq (strXML)
|
||||
) cc
|
||||
LEFT OUTER JOIN (
|
||||
SELECT
|
||||
SCHEMA_NAME(tab.SCHEMA_ID) + '.' + tab.[Name] AS [table]
|
||||
,col.[column_id]
|
||||
,col.[Name] AS [COLUMN_NAME]
|
||||
,CASE
|
||||
WHEN fk.OBJECT_ID IS NOT NULL THEN '>-'
|
||||
ELSE NULL
|
||||
END AS rel
|
||||
,SCHEMA_NAME(pk_tab.SCHEMA_ID) + '.' + pk_tab.[Name] AS primary_table
|
||||
,pk_col.[Name] AS pk_column_name
|
||||
,fk_cols.[constraint_column_id] AS NO
|
||||
,fk.[Name] AS fk_constraint_name
|
||||
,tab.[Name] AS tbl_name
|
||||
FROM [sys].[TABLES] tab
|
||||
INNER JOIN [sys].[COLUMNS] col
|
||||
ON col.OBJECT_ID = tab.OBJECT_ID
|
||||
LEFT OUTER JOIN [sys].[foreign_key_columns] fk_cols
|
||||
ON fk_cols.[parent_object_id] = tab.OBJECT_ID
|
||||
AND fk_cols.[parent_column_id] = col.[column_id]
|
||||
LEFT OUTER JOIN [sys].[foreign_keys] fk
|
||||
ON fk.OBJECT_ID = fk_cols.[constraint_object_id]
|
||||
LEFT OUTER JOIN [sys].[TABLES] pk_tab
|
||||
ON pk_tab.OBJECT_ID = fk_cols.[referenced_object_id]
|
||||
LEFT OUTER JOIN [sys].[COLUMNS] pk_col
|
||||
ON pk_col.[column_id] = fk_cols.[referenced_column_id]
|
||||
AND pk_col.OBJECT_ID = fk_cols.[referenced_object_id]
|
||||
WHERE fk.OBJECT_ID IS NOT NULL
|
||||
) fk
|
||||
ON fk.tbl_name = x.[TblName]
|
||||
AND [c].[IndexColumns] LIKE '%' + fk.[COLUMN_NAME] + '%'
|
||||
WHERE x.[SchemaName] =@schemaName
|
||||
AND x.[TblName] =@tableName
|
||||
AND NOT EXISTS(
|
||||
SELECT 1
|
||||
FROM @indexesToMaintains [itm2]
|
||||
WHERE 1=1
|
||||
AND ISNULL(itm2.[internal_actualColumns],'') = ISNULL(c.[IndexColumns],'')
|
||||
AND ISNULL(itm2.[internal_actualIncludedColumns],'') = ISNULL(ci.[includedColumns],'')
|
||||
)
|
||||
OPTION (RECOMPILE);
|
||||
|
||||
|
||||
FETCH NEXT FROM curIdxName INTO @schemaName, @tableName;
|
||||
END
|
||||
|
||||
CLOSE curIdxName;
|
||||
DEALLOCATE curIdxName;
|
||||
|
||||
|
||||
/* Adjust the "_comp" flags to create a matrix of actions */
|
||||
/* to be created */
|
||||
UPDATE [itm]
|
||||
SET
|
||||
[itm].[isToBeCreated_comp] = CASE
|
||||
WHEN [itm].[isToBeDeleted_inp] = 0 THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
FROM @indexesToMaintains [itm]
|
||||
WHERE [itm].[indexName] IS NULL /* index was not found on the server */
|
||||
AND [itm].[expectedIndexName_inp] IS NOT NULL /* we did gave an index name, ie: we want to create it if missing */
|
||||
;
|
||||
|
||||
/* to be renamed */
|
||||
UPDATE [itm]
|
||||
SET
|
||||
[itm].[isToBeRenamed_comp] = 1
|
||||
FROM @indexesToMaintains [itm]
|
||||
WHERE [itm].[indexName] IS NOT NULL /* index was not found on the server */
|
||||
AND [itm].[expectedIndexName_inp] IS NOT NULL /* we did gave an index name, ie: we expect that index to be present */
|
||||
AND [indexName] <> [itm].[expectedIndexName_inp]
|
||||
AND [isToBeDeleted_inp] = 0
|
||||
AND [isToBeDeleted_comp] = 0
|
||||
;
|
||||
|
||||
/* to be re-created */
|
||||
UPDATE [itm]
|
||||
SET
|
||||
[itm].[isToBeRecreated_comp] = 1
|
||||
FROM @indexesToMaintains [itm]
|
||||
WHERE [isToBeRecreated_inp] = 1
|
||||
;
|
||||
|
||||
/* to be deleted, because specified in table */
|
||||
UPDATE itm
|
||||
SET
|
||||
[itm].[isToBeDeleted_comp] = CASE
|
||||
WHEN [itm].[isIndexFound_comp] = 0 THEN 0
|
||||
ELSE 1
|
||||
END
|
||||
,[itm].[isToBeRecreated_comp] = 0 /* deletion override re-create */
|
||||
,[itm].[isToBeRenamed_comp] = 0 /* deletion override rename */
|
||||
FROM @indexesToMaintains [itm]
|
||||
WHERE [itm].[isToBeDeleted_inp] = 1
|
||||
;
|
||||
|
||||
/* to be deleted, because different to specs. link on index name */
|
||||
UPDATE [exp]
|
||||
SET
|
||||
[exp].[isToBeCreated_comp] = 1 /* flag the expected index "to be created" */
|
||||
FROM @indexesToMaintains [exp] --expected
|
||||
JOIN @indexesToMaintains [act] --actual
|
||||
ON [act].[indexName] = [exp].[expectedIndexName_inp]
|
||||
WHERE [exp].[isToBeDeleted_comp] = 0
|
||||
AND [exp].[isToBeCreated_comp] = 0
|
||||
AND [exp].[isToBeRenamed_comp] = 0
|
||||
AND [exp].[isToBeRecreated_inp] = 0
|
||||
AND (
|
||||
[exp].[columnsName_inp] != [act].[internal_actualColumns]
|
||||
OR [exp].[includedColumns_inp] != [act].[internal_actualIncludedColumns]
|
||||
)
|
||||
;
|
||||
|
||||
UPDATE [act]
|
||||
SET
|
||||
[act].[isToBeDeleted_comp] = 1 /* flag the actual index "to be deleted" */
|
||||
FROM @indexesToMaintains [exp] --expected
|
||||
JOIN @indexesToMaintains [act] --actual
|
||||
ON [act].[indexName] = [exp].[expectedIndexName_inp]
|
||||
WHERE (
|
||||
[exp].[columnsName_inp] != [act].[internal_actualColumns]
|
||||
OR [exp].[includedColumns_inp] != [act].[internal_actualIncludedColumns]
|
||||
)
|
||||
;
|
||||
|
||||
/* to be deleted, because different to specs. link on index columns */
|
||||
UPDATE [exp]
|
||||
SET
|
||||
[exp].[isToBeCreated_comp] = 1 /* flag the expected index "to be created" */
|
||||
FROM @indexesToMaintains [exp] --expected
|
||||
JOIN @indexesToMaintains [act] --actual
|
||||
ON [act].[internal_actualColumns] = [exp].[columnsName_inp]
|
||||
WHERE (
|
||||
[exp].[columnsName_inp] != [act].[internal_actualColumns]
|
||||
OR [exp].[includedColumns_inp] != [act].[internal_actualIncludedColumns]
|
||||
)
|
||||
;
|
||||
UPDATE [act]
|
||||
SET
|
||||
[act].[isToBeDeleted_comp] = 1 /* flag the actual index "to be deleted" */
|
||||
FROM @indexesToMaintains [exp] --expected
|
||||
JOIN @indexesToMaintains [act] --actual
|
||||
ON [act].[internal_actualColumns] = [exp].[columnsName_inp]
|
||||
WHERE (
|
||||
[exp].[columnsName_inp] != [act].[internal_actualColumns]
|
||||
OR [exp].[includedColumns_inp] != [act].[internal_actualIncludedColumns]
|
||||
)
|
||||
;
|
||||
|
||||
/* if we have both "rename" and "delete" actions, remove the "rename" action */
|
||||
UPDATE @indexesToMaintains
|
||||
SET [isToBeRenamed_comp] = 0
|
||||
WHERE [isToBeRenamed_comp] = 1
|
||||
AND [isToBeDeleted_comp] = 1
|
||||
;
|
||||
|
||||
/* ensure that we don't have clash between indexes to create and indexes to rename */
|
||||
UPDATE ir
|
||||
SET [ir].[isToBeRenamed_comp] = 0 /* do not rename this index */
|
||||
, [ir].[isToBeDeleted_inp] = 1 /* drop this index instead, as we have one with the correct structure about to be created */
|
||||
FROM @indexesToMaintains [ic] /* to create */
|
||||
JOIN @indexesToMaintains [ir] /* to rename */
|
||||
ON ic.[expectedIndexName_inp] = ir.[expectedIndexName_inp]
|
||||
WHERE [ic].[isToBeCreated_comp] = 1
|
||||
AND [ir].[isToBeRenamed_comp] = 1
|
||||
|
||||
/* Show the action matrix */
|
||||
SELECT [itm].[isIndexFound_comp]
|
||||
,[itm].[isToBeCreated_comp]
|
||||
,[itm].[isToBeRenamed_comp]
|
||||
,[itm].[isToBeDeleted_comp]
|
||||
,[itm].[isToBeRecreated_comp]
|
||||
,[itm].[internal_statusMsg]
|
||||
,'....'
|
||||
,[itm].[isToBeDeleted_inp]
|
||||
,[itm].[isToBeRecreated_inp]
|
||||
,[itm].[indexName]
|
||||
,[itm].[expectedIndexName_inp]
|
||||
,[itm].[columnsName_inp]
|
||||
,[itm].[internal_actualColumns]
|
||||
,[itm].[includedColumns_inp]
|
||||
,[itm].[internal_actualIncludedColumns]
|
||||
FROM @indexesToMaintains [itm]
|
||||
ORDER BY [itm].[isIndexFound_comp],[itm].[isToBeDeleted_inp], [itm].[columnsName_inp];
|
||||
|
||||
|
||||
/* Drop the indexe(s) that are to be deleted or recreated */
|
||||
SET @query='';
|
||||
SELECT @query = @query + 'DROP INDEX '+[itm].[indexName]+' ON dbo.entry;'+CHAR(13)+CHAR(10)
|
||||
FROM @indexesToMaintains [itm]
|
||||
WHERE [itm].[isToBeDeleted_comp] = 1
|
||||
OR [itm].[isToBeRecreated_comp] = 1
|
||||
;
|
||||
|
||||
PRINT REPLICATE('-', 40);
|
||||
PRINT '-- execute DROP INDEX';
|
||||
PRINT REPLICATE('-', 40);
|
||||
PRINT @query;
|
||||
PRINT REPLICATE('-', 40);
|
||||
PRINT CHAR(13)+CHAR(10);
|
||||
|
||||
IF @DoOnlyOutputStatement = 0
|
||||
BEGIN
|
||||
EXECUTE(@query);
|
||||
END
|
||||
|
||||
/* rename existing indexe(s) */
|
||||
SET @query = '';
|
||||
SELECT @query = @query + 'EXEC sp_rename @objname ='''+[itm].[tableName]+'.'+[itm].[indexName]+''', @newname = '''+[itm].[expectedIndexName_inp]+''';'+CHAR(13)+CHAR(10)
|
||||
FROM @indexesToMaintains [itm]
|
||||
WHERE [itm].[isToBeRenamed_comp] = 1
|
||||
;
|
||||
|
||||
PRINT REPLICATE('-', 40);
|
||||
PRINT '-- execute sp_rename';
|
||||
PRINT REPLICATE('-', 40);
|
||||
PRINT @query;
|
||||
PRINT REPLICATE('-', 40);
|
||||
PRINT CHAR(13)+CHAR(10);
|
||||
|
||||
IF @DoOnlyOutputStatement = 0
|
||||
BEGIN
|
||||
EXECUTE(@query);
|
||||
END
|
||||
|
||||
/* create indexe(s) */
|
||||
SET @query='';
|
||||
SELECT @query = @query + 'CREATE '
|
||||
+CASE WHEN [itm].[isClustered_inp]=1 THEN 'CLUSTERED' ELSE 'NONCLUSTERED' END
|
||||
+CASE WHEN [itm].[isUnique_inp]=1 THEN ' UNIQUE' ELSE '' END
|
||||
+' INDEX '+[itm].[expectedIndexName_inp]
|
||||
+' ON '+[itm].[schemaName]+'.'+[itm].[tableName]+'('+[itm].[columnsName_inp]+')'
|
||||
+ CASE
|
||||
WHEN [itm].[includedColumns_inp] IS NOT NULL THEN ' INCLUDE ('+[itm].[includedColumns_inp]+')'
|
||||
ELSE ''
|
||||
END
|
||||
+';'
|
||||
+CHAR(13)+CHAR(10)
|
||||
FROM @indexesToMaintains [itm]
|
||||
WHERE (
|
||||
[itm].[isToBeCreated_comp] = 1
|
||||
OR [itm].[isToBeRecreated_comp] = 1
|
||||
)
|
||||
;
|
||||
|
||||
PRINT REPLICATE('-', 40);
|
||||
PRINT '-- execute CREATE INDEX';
|
||||
PRINT REPLICATE('-', 40);
|
||||
PRINT @query;
|
||||
PRINT REPLICATE('-', 40);
|
||||
PRINT CHAR(13)+CHAR(10);
|
||||
|
||||
IF @DoOnlyOutputStatement = 0
|
||||
BEGIN
|
||||
EXECUTE(@query);
|
||||
END
|
||||
|
||||
--ROLLBACK TRANSACTION;
|
||||
COMMIT TRANSACTION
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user