Technical Article

Generate Create FK-indexes

,

Our standard is to always create foreign key indexes.
These indexes exactly match the columns of the DRI-definition.
This is to avoid scans when parent rows are being deleted.

Stored procedure [Spc_ALZDBA_Create_FK_Ix] will only script the "CREATE INDEX" statements.
You'll have to copy/paste and execute yourself, based on your own preferences.

This select statement will generate exec-statements for all base tables.
Copy/paste and execute or declare a cursor and execute immediate.

-- Execution example
select 'exec Spc_ALZDBA_Create_FK_Ix @ParentTbName = '''+ T.TABLE_NAME + ''' ,@ParentTbOwner = ''' + T.TABLE_SCHEMA + ''', @CheckExistIx = 1 ' + char(10) + 'go'
from INFORMATION_SCHEMA.Tables T
where T.TABLE_TYPE = 'BASE TABLE'
order by T.TABLE_SCHEMA, T.TABLE_NAME

-- Execution example for the procedure
exec Spc_ALZDBA_Create_FK_Ix @ParentTbName = 'Address' ,@ParentTbOwner = 'Person', @CheckExistIx = 1

-- result:
Create index [XFK_ALZ_FK_EmployeeAddress_Address_AddressID] on [HumanResources].[EmployeeAddress] ( AddressID ) ;
GO

This procedure will only script the "CREATE INDEX" statements.
You'll have to copy/paste and execute yourself, based on your own preferences.

TEST IT - TEST IT - TEST IT - TEST IT

-- drop procedure Spc_ALZDBA_Create_FK_Ix
-- go
Create procedure Spc_ALZDBA_Create_FK_Ix
     @ParentTbName varchar(128)
    , @ParentTbOwner varchar(128) = NULL
    , @CheckExistIx bit = 1
-- with encryption
as 
begin
-- Execution example
--     select 'exec Spc_ALZDBA_Create_FK_Ix @ParentTbName = '''+ T.TABLE_NAME + ''' ,@ParentTbOwner = ''' + T.TABLE_SCHEMA + ''', @CheckExistIx = 1 ' + char(10) + 'go'
--     from     INFORMATION_SCHEMA.Tables T 
--     where     T.TABLE_TYPE = 'BASE TABLE'
--     order by T.TABLE_SCHEMA, T.TABLE_NAME 

-- as

-- creatie test case begin
-- if object_id('T_ParentMC') is null
-- begin
--     create table T_ParentMC (
--     col1 int identity(1,1) not null ,
--     col2 int not null,
--     col3 int not null,
--     col4 int not null,
--     col5 varchar(100) not null default ''
--     , CONSTRAINT PK_ParentMC PRIMARY KEY (col2,col3,col4)
--     )
--     print 'table T_ParentMC created'
-- end 
-- go
-- 
-- if object_id('T_ChildMC') is null
-- begin
--     create table T_ChildMC (
--     col1 int identity(1,1) not null primary key,
--     col2FK1_1 int not null,
--     col3FK1_2 int not null,
--     col4FK1_3 int not null,
--     col5 varchar(100) not null default ''
--     , CONSTRAINT FK_C2P FOREIGN KEY (col2FK1_1, col3FK1_2, col4FK1_3) 
--         REFERENCES T_ParentMC (col2,col3,col4)
--     )
--     print 'table T_ChildMC created'
-- end
-- go
-- -- creatie test case einde
-- 
-- 
-- Declare @ParentTbName varchar(128) 
--     , @ParentTbOwner varchar(128)
-- select @ParentTbName = 'T_ParentMC', @ParentTbOwner = NULL

SET nocount on

create table #tmpPKeys(
TABLE_QUALIFIER sysname, 
TABLE_OWNER sysname not null,
TABLE_NAME sysname not null,
COLUMN_NAME sysname not null,
KEY_SEQ smallint not null,
PK_NAME sysname null )

Create index ix#tmpPKeys on #tmpPKeys (TABLE_QUALIFIER, TABLE_OWNER,TABLE_NAME, KEY_SEQ)


-- Get PK-info
insert into #tmpPKeys
exec sp_pkeys @table_name = @ParentTbName
    , @table_owner = @ParentTbOwner
-- [ , [ @table_qualifier = ] 'qualifier' ] -- DBName

create table #tmpFKeys 
(PKTABLE_QUALIFIER sysname not null, 
PKTABLE_OWNER sysname not null, 
PKTABLE_NAME sysname not null, 
PKCOLUMN_NAME sysname not null, 
FKTABLE_QUALIFIER sysname not null, 
FKTABLE_OWNER sysname not null,
FKTABLE_NAME sysname not null,
FKCOLUMN_NAME sysname not null,
KEY_SEQ smallint not null,
UPDATE_RULE smallint not null,
DELETE_RULE smallint not null,
FK_NAME sysname not null,
PK_NAME sysname not null,
DEFERRABILITY int not null)

Create index #tmpFKeys on #tmpFKeys (FK_NAME, KEY_SEQ)


-- Get FK-info (all dependant objects)
insert into #tmpFKeys
exec sp_fkeys @pktable_name = @ParentTbName
    , @pktable_owner = @ParentTbOwner
-- [ , [ @pktable_qualifier = ] 'pktable_qualifier' ] 
-- { , [ @fktable_name = ] 'fktable_name' } 
-- [ , [ @fktable_owner = ] 'fktable_owner' ] 
-- [ , [ @fktable_qualifier = ] 'fktable_qualifier' ]

-- print 'Begin transaction trxCreateIx_' + @ParentTbName 
-- print ' ' 

-- Detect indexes for dependant tables

create table #tmpExistIx ( FKTABLE_OWNER varchar(128) not null default '-------------------------------------------',
                        FKTABLE_NAME varchar(128) not null default '-------------------------------------------',
                        index_name varchar(128) not null,
                        index_description varchar(512) not null,
                        index_keys varchar(512) not null )

declare @FQTable varchar(257)
        , @FKTABLE_OWNER varchar(128)
        , @FKTABLE_NAME varchar(128)
declare csrHelpIx cursor 
    for select FKTABLE_OWNER, FKTABLE_NAME, FKTABLE_OWNER + '.' + FKTABLE_NAME as FQTable from #tmpFKeys
open csrHelpIx

FETCH NEXT FROM csrHelpIx INTO @FKTABLE_OWNER, @FKTABLE_NAME, @FQTable

WHILE @@FETCH_STATUS = 0
BEGIN
    insert into #tmpExistIx (index_name ,index_description, index_keys )
        exec sp_helpindex @FQTable

    update #tmpExistIx 
        set FKTABLE_OWNER = @FKTABLE_OWNER
            , FKTABLE_NAME = @FKTABLE_NAME
    where FKTABLE_OWNER = '-------------------------------------------'

    FETCH NEXT FROM csrHelpIx INTO @FKTABLE_OWNER, @FKTABLE_NAME, @FQTable

END

-- Cursor afsluiten
CLOSE csrHelpIx
DEALLOCATE csrHelpIx

-- print '-- Create FK-Indexes'
-- print '-- keep FK column-ordinal / order equal to PK column-ordinal / order (asc/desc)'
select 'Create index [XFK_ALZ_' + FK1.FK_NAME + '] on [' + FK1.FKTABLE_OWNER + '].[' + FK1.FKTABLE_NAME + '] ( '+
FK1.FKCOLUMN_NAME
+ case when FK2.FKCOLUMN_NAME is null then '' else ', ' + FK2.FKCOLUMN_NAME end 
+ case when FK3.FKCOLUMN_NAME is null then '' else ', ' + FK3.FKCOLUMN_NAME end 
+ case when FK4.FKCOLUMN_NAME is null then '' else ', ' + FK4.FKCOLUMN_NAME end 
+ case when FK5.FKCOLUMN_NAME is null then '' else ', ' + FK5.FKCOLUMN_NAME end 
+ case when FK6.FKCOLUMN_NAME is null then '' else ', ' + FK6.FKCOLUMN_NAME end 
+ case when FK7.FKCOLUMN_NAME is null then '' else ', ' + FK7.FKCOLUMN_NAME end 
+ case when FK8.FKCOLUMN_NAME is null then '' else ', ' + FK8.FKCOLUMN_NAME end 
+ case when FK9.FKCOLUMN_NAME is null then '' else ', ' + FK9.FKCOLUMN_NAME end 
+ case when FK10.FKCOLUMN_NAME is null then '' else ', ' + FK10.FKCOLUMN_NAME end 
+ case when FK11.FKCOLUMN_NAME is null then '' else ', ' + FK11.FKCOLUMN_NAME end 
+ case when FK12.FKCOLUMN_NAME is null then '' else ', ' + FK12.FKCOLUMN_NAME end 
+ case when FK13.FKCOLUMN_NAME is null then '' else ', ' + FK13.FKCOLUMN_NAME end 
+ case when FK14.FKCOLUMN_NAME is null then '' else ', ' + FK14.FKCOLUMN_NAME end
+ case when FK15.FKCOLUMN_NAME is null then '' else ', ' + FK15.FKCOLUMN_NAME end 
+ case when FK16.FKCOLUMN_NAME is null then '' else ', ' + FK16.FKCOLUMN_NAME end 
+ case when FK17.FKCOLUMN_NAME is null then '' else ', ' + FK17.FKCOLUMN_NAME end 
+ case when FK18.FKCOLUMN_NAME is null then '' else ', ' + FK18.FKCOLUMN_NAME end 
+ ' ) ;' + char(10) + 'GO ' 
from #tmpFKeys FK1 
left join #tmpFKeys FK2
    on FK1.FK_NAME = FK2.FK_NAME and FK1.KEY_SEQ = 1 and FK2.KEY_SEQ = 2
left join #tmpFKeys FK3
    on FK1.FK_NAME = FK3.FK_NAME and FK1.KEY_SEQ = 1 and FK3.KEY_SEQ = 3
left join #tmpFKeys FK4
    on FK1.FK_NAME = FK4.FK_NAME and FK1.KEY_SEQ = 1 and FK4.KEY_SEQ = 4
left join #tmpFKeys FK5
    on FK1.FK_NAME = FK5.FK_NAME and FK1.KEY_SEQ = 1 and FK5.KEY_SEQ = 5
left join #tmpFKeys FK6
    on FK1.FK_NAME = FK6.FK_NAME and FK1.KEY_SEQ = 1 and FK6.KEY_SEQ = 6
left join #tmpFKeys FK7
    on FK1.FK_NAME = FK7.FK_NAME and FK1.KEY_SEQ = 1 and FK7.KEY_SEQ = 7
left join #tmpFKeys FK8 
    on FK1.FK_NAME = FK8.FK_NAME and FK1.KEY_SEQ = 1 and FK8.KEY_SEQ = 8
left join #tmpFKeys FK9
    on FK1.FK_NAME = FK9.FK_NAME and FK1.KEY_SEQ = 1 and FK9.KEY_SEQ = 9
left join #tmpFKeys FK10
    on FK1.FK_NAME = FK10.FK_NAME and FK1.KEY_SEQ = 1 and FK10.KEY_SEQ = 10
left join #tmpFKeys FK11
    on FK1.FK_NAME = FK11.FK_NAME and FK1.KEY_SEQ = 1 and FK11.KEY_SEQ = 11
left join #tmpFKeys FK12
    on FK1.FK_NAME = FK12.FK_NAME and FK1.KEY_SEQ = 1 and FK12.KEY_SEQ = 12
left join #tmpFKeys FK13
    on FK1.FK_NAME = FK13.FK_NAME and FK1.KEY_SEQ = 1 and FK13.KEY_SEQ = 13
left join #tmpFKeys FK14
    on FK1.FK_NAME = FK14.FK_NAME and FK1.KEY_SEQ = 1 and FK14.KEY_SEQ = 14
left join #tmpFKeys FK15
    on FK1.FK_NAME = FK15.FK_NAME and FK1.KEY_SEQ = 1 and FK15.KEY_SEQ = 15
left join #tmpFKeys FK16
    on FK1.FK_NAME = FK16.FK_NAME and FK1.KEY_SEQ = 1 and FK16.KEY_SEQ = 16
left join #tmpFKeys FK17
    on FK1.FK_NAME = FK17.FK_NAME and FK1.KEY_SEQ = 1 and FK17.KEY_SEQ = 17
left join #tmpFKeys FK18
    on FK1.FK_NAME = FK18.FK_NAME and FK1.KEY_SEQ = 1 and FK18.KEY_SEQ = 18

where FK1.KEY_SEQ = 1
AND ( @CheckExistIx <> 1
     OR NOT EXISTS (select * from #tmpExistIx FKIx 
                            where FKIx.FKTABLE_OWNER = FK1.FKTABLE_OWNER
                             and FKIx.FKTABLE_NAME = FK1.FKTABLE_NAME 
                                and FKIx.index_keys like FK1.FKCOLUMN_NAME + '%' )
    )
order by FK1.FKTABLE_OWNER, FK1.FKTABLE_NAME, FK1.FK_NAME


-- print 'Commit transaction trxCreateIx_' + @ParentTbName 

-- cleanup
drop table #tmpPKeys
drop table #tmpFKeys
drop table #tmpExistIx

-- cleanup when testing is done
-- drop table T_ChildMC
-- drop table T_ParentMC
-- 
end

Rate

1 (2)

You rated this post out of 5. Change rating

Share

Share

Rate

1 (2)

You rated this post out of 5. Change rating