Wednesday, 15 October 2014

Transact-SQL Generate missing foreign key constraints for data warehouse

-- Based on http://richarddingwall.name/2008/12/21/find-missing-foreignprimary-keys-in-sql-server/ Thanks!
-- Pretty printed with http://www.dpriver.com/pp/sqlformat.htm Thanks!
-- Find columns with names like K% on %_Fact tables which:
-- are also a primary key column name on a '%_Dim' table 
-- and aren't part of the primary key
-- and should probably be foreign keys, but aren't.
-- Updates: NOCHECK * WITH CHECK
--
-- Limitations:
-- Sorry, not designed for compound primary keys.
-- Only works with DBO schema
SELECT 'ALTER TABLE ' + Quotename(Object_name(t.object_id)) + ' WITH NOCHECK ADD  CONSTRAINT [FK_' + Object_name(t.object_id) + '_' + rpkc.NAME + '] ' + 'FOREIGN KEY([' + rpkc.NAME + ']) REFERENCES [' + pkt.NAME + '] ([' + rpkc.NAME + ']) ;' + Char(10) AS SQLCreateConstraint
  ,'ALTER TABLE ' + Quotename(Object_name(t.object_id)) + ' WITH CHECK CHECK CONSTRAINT [FK_' + Object_name(t.object_id) + '_' + rpkc.NAME + '] ;' + Char(10)  AS SQLCheckConstraint
FROM sys.tables t
INNER JOIN sys.syscolumns c ON c.id = t.object_id
-- And it's a primary key elsewhere
INNER JOIN sys.columns AS rpkc ON c.NAME = rpkc.NAME
INNER JOIN sys.index_columns AS pkic ON pkic.object_id = rpkc.object_id
  AND pkic.column_id = rpkc.column_id
INNER JOIN sys.indexes AS pki ON pki.object_id = pkic.object_id
  AND pki.index_id = pkic.index_id
  AND pki.is_primary_key = 1
INNER JOIN sys.tables AS pkt ON pki.object_id = pkt.object_id
-- Join on foreign key columns
LEFT JOIN sys.foreign_key_columns fkc ON (
    fkc.parent_object_id = t.object_id
    AND c.colid = fkc.parent_column_id
    )
  OR (
    fkc.referenced_object_id = t.object_id
    AND c.colid = fkc.referenced_column_id
    )
-- Join on primary key columns
LEFT JOIN sys.indexes i ON i.object_id = t.object_id
  AND i.is_primary_key = 1
LEFT JOIN sys.index_columns ic ON ic.object_id = t.object_id
  AND ic.index_id = i.index_id
  AND ic.column_id = c.colid
WHERE t.is_ms_shipped = 0
  AND (c.NAME LIKE 'K%')
  AND (
    fkc.constraint_object_id IS NULL -- Not part of a foreign key 
    AND ic.object_id IS NULL -- Not part of a primary key
    )
  AND t.NAME LIKE '%[_]Fact'
  AND pkt.NAME LIKE '%[_]Dim'
  AND (
    -- Ignore some tables
    t.NAME != 'sysdiagrams'
    AND t.NAME NOT LIKE '[_]%' -- temp tables
    AND t.NAME NOT LIKE '%temp%'
    AND t.NAME NOT LIKE '%Log%' -- log tables
    AND t.NAME NOT LIKE '%[_]bak' -- _bak tables
    -- Ignore some columns
    AND c.NAME NOT IN ('xxx') -- external keys
    )
  AND Object_schema_name(t.object_id) = 'dbo'
  AND Object_schema_name(pkt.object_id) = 'dbo'
ORDER BY t.NAME
  ,c.NAME

1 comment:

  1. Edited to add with NOCHECK and WITH CHECK. Thanks http://www.brentozar.com/blitz/foreign-key-trusted/

    ReplyDelete