796 lines
34 KiB
Transact-SQL
796 lines
34 KiB
Transact-SQL
IF OBJECT_ID('upd.alignIndexes') IS NOT NULL
|
|
DROP PROCEDURE [upd].alignIndexes;
|
|
GO
|
|
|
|
/*=============================================================================
|
|
|
|
OCTPDBA-414
|
|
This procedure is used to align indexes in a pharmacy against a template, stored in the table [arizona].[upd].[IndexesAlignementActions]
|
|
|
|
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
|
|
|
|
Parameters
|
|
----------------------
|
|
@in_debug
|
|
Debug level. Default 0
|
|
0 = no debug, execute commands
|
|
1 = print commands and output a matrix showing the found state
|
|
2 = only print commands and output a matrix showing the found state
|
|
|
|
@in_dropUnreferencedIndexes
|
|
Drop indexes not referenced. Default 0
|
|
This flag govern behavior when we have a index on a table that exists in [IndexesAlignementActions], but that this specific index is not present in [IndexesAlignementActions]
|
|
0 = Leave the unknown index in place
|
|
1 = Drop the unknown index
|
|
|
|
Context
|
|
----------------------
|
|
This proc can be called at anytime to align indexes against those referenced in [arizona].[upd].[IndexesAlignementActions]
|
|
|
|
Creation : 03.02.2023 / TSC
|
|
Modifications:
|
|
|
|
=============================================================================*/
|
|
CREATE PROCEDURE [upd].[alignIndexes]
|
|
@in_debug INT = 0,
|
|
@in_dropUnreferencedIndexes BIT = 0
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
|
|
/* variables */
|
|
DECLARE @query VARCHAR(MAX); /* for dynamic sql */
|
|
DECLARE @outputStatement BIT = CASE /* Do we want to have the commands printed out ? */
|
|
WHEN @in_debug IN ( 1, 2 ) THEN
|
|
1
|
|
ELSE
|
|
0
|
|
END;
|
|
DECLARE @executeStatement BIT = CASE /* Do we want to execute the commands ? */
|
|
WHEN @in_debug IN ( 0, 1 ) THEN
|
|
1
|
|
ELSE
|
|
0
|
|
END;
|
|
DECLARE @schemaName VARCHAR(100); /* used in cursor */
|
|
DECLARE @tableName VARCHAR(100); /* used in cursor */
|
|
DECLARE @indexFK INT; /* used in cursor */
|
|
DECLARE @msg VARCHAR(MAX) = '';
|
|
DECLARE @indexesToMaintains TABLE /* working representation of upd.IA_IndexesAlignementActions */
|
|
(
|
|
[IA_indexesAlignementActions] INT NULL, /* FK to table upd.IA_IndexesAlignementActions */
|
|
[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 */
|
|
IA_index_options VARCHAR(MAX) NULL, /* index options */
|
|
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 ? */
|
|
includedColumnsMatch_internal BIT NULL, /* computed ba the script. Are the expected and actual included columns identical ? */
|
|
actualColumns_internal VARCHAR(MAX) NULL, /* the actual columns in the existing index */
|
|
actualIncludedColumns_internal VARCHAR(MAX) NULL, /* the actual columns in the INCLUDE part of the index */
|
|
statusMsg_internal VARCHAR(MAX) NULL /* Message, used for debug */
|
|
);
|
|
|
|
|
|
/* fetch index to align from upd.IA_IndexesAlignementActions */
|
|
INSERT INTO @indexesToMaintains
|
|
(
|
|
[IA_indexesAlignementActions],
|
|
[SchemaName],
|
|
[TableName],
|
|
[expectedIndexName_inp],
|
|
[columnsName_inp],
|
|
[includedColumns_inp],
|
|
[isClustered_inp],
|
|
[isUnique_inp],
|
|
[isToBeDeleted_inp],
|
|
[isToBeRecreated_inp],
|
|
[IA_index_options]
|
|
)
|
|
SELECT ia.[IA_indexesAlignementActionsID],
|
|
ia.[IA_schemaName],
|
|
ia.[IA_tableName],
|
|
ia.[IA_expectedIndexName],
|
|
ia.[IA_columnsName],
|
|
ia.[IA_includedColumns],
|
|
ia.[IA_isClustered],
|
|
ia.[IA_isUnique],
|
|
ia.[IA_isToBeDeleted],
|
|
ia.[IA_isToBeRecreated],
|
|
ia.[IA_indexOptions]
|
|
FROM [upd].[IA_IndexesAlignementActions] ia
|
|
WHERE ia.[IA_executionDate] IS NULL;
|
|
|
|
|
|
/* logic checks on index definition table */
|
|
SELECT @msg
|
|
= @msg + 'Duplicate index (on expected index name) found in indexes definition. Table ['
|
|
+ [itm].[IA_schemaName] + '].[' + [itm].[IA_tableName] + '], expected index name ['
|
|
+ [itm].[IA_expectedIndexName] + ']' + CHAR(13) + CHAR(10)
|
|
FROM [upd].[IA_IndexesAlignementActions] [itm]
|
|
JOIN
|
|
(
|
|
SELECT [ii].[IA_expectedIndexName] AS [key]
|
|
FROM [upd].[IA_IndexesAlignementActions] [ii]
|
|
GROUP BY [ii].[IA_expectedIndexName]
|
|
HAVING COUNT(1) > 1
|
|
) d
|
|
ON d.[key] = [itm].[IA_expectedIndexName]
|
|
GROUP BY [itm].[IA_schemaName],
|
|
[itm].[IA_tableName],
|
|
[itm].[IA_expectedIndexName];
|
|
|
|
--duplicate index key
|
|
SELECT @msg
|
|
= @msg + 'Duplicate index (on column definition) found in indexes definition. Table [' + [itm].[IA_schemaName]
|
|
+ '].[' + [itm].[IA_tableName] + '], columns in index: ' + [itm].[IA_columnsName]
|
|
+ CASE
|
|
WHEN [itm].[IA_includedColumns] IS NOT NULL THEN
|
|
', included colum(s): ' + [itm].[IA_includedColumns]
|
|
ELSE
|
|
''
|
|
END + CHAR(13) + CHAR(10)
|
|
FROM [upd].[IA_IndexesAlignementActions] [itm]
|
|
JOIN
|
|
(
|
|
SELECT [ii].[IA_columnsName] + '_' + ISNULL([ii].[IA_includedColumns], '') AS [key]
|
|
FROM [upd].[IA_IndexesAlignementActions] [ii]
|
|
GROUP BY [ii].[IA_columnsName] + '_' + ISNULL([ii].[IA_includedColumns], '')
|
|
HAVING COUNT(1) > 1
|
|
) d
|
|
ON d.[key] = [itm].[IA_columnsName] + '_' + ISNULL([itm].[IA_includedColumns], '')
|
|
GROUP BY [itm].[IA_schemaName],
|
|
[itm].[IA_tableName],
|
|
[itm].[IA_columnsName],
|
|
[itm].[IA_includedColumns];
|
|
|
|
IF @msg <> ''
|
|
BEGIN
|
|
RAISERROR(@msg, 16, 4);
|
|
IF @@TRANCOUNT > 0
|
|
BEGIN
|
|
ROLLBACK TRANSACTION;
|
|
END;
|
|
RETURN 1;
|
|
END;
|
|
|
|
|
|
/* fetch the indexes name and non listed indexes on the current instance */
|
|
IF CURSOR_STATUS('global','curIdxName')>=-1
|
|
BEGIN
|
|
DEALLOCATE curIdxName
|
|
END
|
|
DECLARE curIdxName CURSOR LOCAL FAST_FORWARD READ_ONLY FOR
|
|
SELECT DISTINCT
|
|
[itm].[IA_schemaName],
|
|
[itm].[IA_tableName],
|
|
itm.[IA_indexesAlignementActionsID]
|
|
FROM [upd].[IA_IndexesAlignementActions] [itm];
|
|
|
|
OPEN curIdxName;
|
|
|
|
FETCH NEXT FROM curIdxName
|
|
INTO @schemaName,
|
|
@tableName,
|
|
@indexFK;
|
|
|
|
WHILE @@FETCH_STATUS = 0
|
|
BEGIN
|
|
/* map the list to the actual indexes */
|
|
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,
|
|
i.type AS indexType /* 1=clustered, 2=non clustered */
|
|
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])
|
|
UPDATE [itm]
|
|
SET [itm].[indexName] = i.[name],
|
|
[itm].[includedColumnsMatch_internal] = CASE
|
|
WHEN ISNULL([itm].[includedColumns_inp], '') = ISNULL(
|
|
[idx_cols].[includedColumns],
|
|
''
|
|
) THEN
|
|
1
|
|
ELSE
|
|
0
|
|
END,
|
|
[itm].[actualIncludedColumns_internal] = [idx_cols].[includedColumns],
|
|
[itm].[actualColumns_internal] = [idx_cols].[IndexColumns],
|
|
[itm].[isIndexFound_comp] = 1,
|
|
[itm].[isToBeCreated_comp] = 0,
|
|
[itm].[isToBeRecreated_comp] = CASE
|
|
WHEN idx_cols.indexType <> 1
|
|
AND [itm].isClustered_inp = 1
|
|
AND idx_cols.IsPrimaryKey = 0 THEN
|
|
1 /* existing index is not clustered and not the PK, recreate */
|
|
ELSE
|
|
0
|
|
END
|
|
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,
|
|
x.indexType
|
|
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],
|
|
[statusMsg_internal],
|
|
[actualIncludedColumns_internal],
|
|
[actualColumns_internal],
|
|
[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.[actualColumns_internal], '')
|
|
OR ISNULL(itm.[includedColumns_inp], '') <> ISNULL(
|
|
itm.[actualIncludedColumns_internal],
|
|
''
|
|
)
|
|
) THEN
|
|
1
|
|
WHEN @in_dropUnreferencedIndexes = 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.[actualColumns_internal], '')
|
|
OR ISNULL(itm.[includedColumns_inp], '') <> ISNULL(
|
|
itm.[actualIncludedColumns_internal],
|
|
''
|
|
)
|
|
) THEN
|
|
'Index present locally not as expected, drop before creation'
|
|
WHEN @in_dropUnreferencedIndexes = 1 THEN
|
|
'Index present locally only'
|
|
ELSE
|
|
NULL
|
|
END AS [statusMsg_internal],
|
|
ci.[IncludedColumns] AS [actualIncludedColumns_internal],
|
|
c.[IndexColumns] AS [actualColumns_internal],
|
|
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.[actualColumns_internal], '') = ISNULL(c.[IndexColumns], '')
|
|
AND ISNULL(itm2.[actualIncludedColumns_internal], '') = ISNULL(ci.[IncludedColumns], '')
|
|
)
|
|
OPTION (RECOMPILE);
|
|
|
|
|
|
FETCH NEXT FROM curIdxName
|
|
INTO @schemaName,
|
|
@tableName,
|
|
@indexFK;
|
|
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
|
|
AND [isToBeRecreated_inp] = 0
|
|
AND [isToBeRecreated_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].[actualColumns_internal]
|
|
OR [exp].[includedColumns_inp] <> [act].[actualIncludedColumns_internal]
|
|
);
|
|
|
|
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].[actualColumns_internal]
|
|
OR [exp].[includedColumns_inp] <> [act].[actualIncludedColumns_internal]
|
|
);
|
|
|
|
/* 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].[actualColumns_internal] = [exp].[columnsName_inp]
|
|
WHERE (
|
|
[exp].[columnsName_inp] <> [act].[actualColumns_internal]
|
|
OR [exp].[includedColumns_inp] <> [act].[actualIncludedColumns_internal]
|
|
);
|
|
|
|
UPDATE [act]
|
|
SET [act].[isToBeDeleted_comp] = 1 /* flag the actual index "to be deleted" */
|
|
FROM @indexesToMaintains [exp] --expected
|
|
JOIN @indexesToMaintains [act] --actual
|
|
ON [act].[actualColumns_internal] = [exp].[columnsName_inp]
|
|
WHERE (
|
|
[exp].[columnsName_inp] <> [act].[actualColumns_internal]
|
|
OR [exp].[includedColumns_inp] <> [act].[actualIncludedColumns_internal]
|
|
);
|
|
|
|
/* 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 */
|
|
IF @in_debug > 0
|
|
BEGIN
|
|
SELECT [itm].[isIndexFound_comp],
|
|
[itm].[isToBeCreated_comp],
|
|
[itm].[isToBeRenamed_comp],
|
|
[itm].[isToBeDeleted_comp],
|
|
[itm].[isToBeRecreated_comp],
|
|
[itm].[statusMsg_internal],
|
|
[itm].[IA_index_options],
|
|
'....' AS separator,
|
|
[itm].[isToBeDeleted_inp],
|
|
[itm].[isToBeRecreated_inp],
|
|
[itm].[indexName],
|
|
[itm].[expectedIndexName_inp],
|
|
[itm].[columnsName_inp],
|
|
[itm].[actualColumns_internal],
|
|
[itm].[includedColumns_inp],
|
|
[itm].[actualIncludedColumns_internal]
|
|
FROM @indexesToMaintains [itm]
|
|
ORDER BY [itm].[isIndexFound_comp],
|
|
[itm].[isToBeDeleted_inp],
|
|
[itm].[columnsName_inp];
|
|
END;
|
|
|
|
|
|
/* Drop the indexe(s) that are to be deleted or recreated */
|
|
SET @query = '';
|
|
SELECT @query
|
|
= @query + 'DROP INDEX ' + [itm].[indexName] + ' ON ' + [itm].[SchemaName] + '.' + [itm].[TableName] + ';'
|
|
+ CHAR(13) + CHAR(10)
|
|
FROM @indexesToMaintains [itm]
|
|
WHERE [itm].[isToBeDeleted_comp] = 1
|
|
OR [itm].[isToBeRecreated_comp] = 1;
|
|
|
|
IF @outputStatement = 1
|
|
BEGIN
|
|
PRINT REPLICATE('-', 40);
|
|
PRINT '-- execute DROP INDEX';
|
|
PRINT REPLICATE('-', 40);
|
|
PRINT @query;
|
|
PRINT REPLICATE('-', 40);
|
|
PRINT CHAR(13) + CHAR(10);
|
|
END;
|
|
|
|
IF @executeStatement = 1
|
|
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;
|
|
|
|
IF @outputStatement = 1
|
|
BEGIN
|
|
PRINT REPLICATE('-', 40);
|
|
PRINT '-- execute sp_rename';
|
|
PRINT REPLICATE('-', 40);
|
|
PRINT @query;
|
|
PRINT REPLICATE('-', 40);
|
|
PRINT CHAR(13) + CHAR(10);
|
|
END;
|
|
|
|
IF @executeStatement = 1
|
|
BEGIN
|
|
EXECUTE (@query);
|
|
END;
|
|
|
|
|
|
/* create indexe(s) */
|
|
SET @query = '';
|
|
SELECT @query
|
|
= @query + 'CREATE ' + CASE
|
|
WHEN [itm].[isUnique_inp] = 1 THEN
|
|
'UNIQUE '
|
|
ELSE
|
|
''
|
|
END + CASE
|
|
WHEN [itm].[isClustered_inp] = 1 THEN
|
|
'CLUSTERED'
|
|
ELSE
|
|
'NONCLUSTERED'
|
|
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) + COALESCE('WITH(' + [itm].[IA_index_options] + ')' + CHAR(13) + CHAR(10), '')
|
|
+ ';' + CHAR(13) + CHAR(10)
|
|
FROM @indexesToMaintains [itm]
|
|
WHERE (
|
|
[itm].[isToBeCreated_comp] = 1
|
|
OR [itm].[isToBeRecreated_comp] = 1
|
|
);
|
|
|
|
IF @outputStatement = 1
|
|
BEGIN
|
|
PRINT REPLICATE('-', 40);
|
|
PRINT '-- execute CREATE INDEX';
|
|
PRINT REPLICATE('-', 40);
|
|
PRINT @query;
|
|
PRINT REPLICATE('-', 40);
|
|
PRINT CHAR(13) + CHAR(10);
|
|
END;
|
|
|
|
IF @executeStatement = 1
|
|
BEGIN
|
|
EXECUTE (@query);
|
|
END;
|
|
|
|
/* record results in upd.IA_IndexesAlignementActions */
|
|
UPDATE ia
|
|
SET ia.[IA_executionDate] = CURRENT_TIMESTAMP,
|
|
ia.[IA_foundIndexName] = ii.indexName,
|
|
ia.[IA_wasIndexFound] = ii.isIndexFound_comp,
|
|
ia.[IA_wasCreated] = ii.isToBeCreated_comp,
|
|
ia.[IA_wasRenamed] = ii.isToBeRenamed_comp,
|
|
ia.[IA_wasDeleted] = ii.isToBeDeleted_comp,
|
|
ia.[IA_wasRecreated] = ii.isToBeRecreated_comp,
|
|
ia.[IA_includedColumnsMatch] = ii.includedColumnsMatch_internal,
|
|
ia.[IA_actualColumns] = ii.actualColumns_internal,
|
|
ia.[IA_actualIncludedColumns] = ii.actualIncludedColumns_internal,
|
|
ia.[IA_statusMsg] = ii.statusMsg_internal
|
|
FROM [upd].[IA_IndexesAlignementActions] ia
|
|
JOIN @indexesToMaintains ii
|
|
ON ii.[IA_indexesAlignementActions] = ia.[IA_indexesAlignementActionsID]
|
|
WHERE ia.[IA_executionDate] IS NULL;
|
|
END;
|
|
GO
|