/*
Author: Alexandros Pappas
Original link: http://www.codeproject.com/Articles/1118722/SQL-Table-Hierarchy
*/

DECLARE @fkcolumns TABLE(name SYSNAME PRIMARY KEY, referencedtable SYSNAME, parenttable SYSNAME, referencedcolumns varchar(MAX), parentcolumns varchar(MAX))
INSERT @fkcolumns
SELECT 
    a.name,
    b.name,
    c.name,
    STUFF((
        SELECT ',' + c.name
        FROM sys.foreign_key_columns b
        INNER JOIN sys.columns c ON b.referenced_object_id = c.object_id
        AND b.referenced_column_id = c.column_id
        WHERE a.object_id = b.constraint_object_id
        FOR XML PATH('')), 1, 1, '') parentcolumns,
    STUFF((
        SELECT ',' + c.name
        FROM sys.foreign_key_columns b
        INNER JOIN sys.columns c ON b.parent_object_id = c.object_id
        AND b.parent_column_id = c.column_id
        WHERE a.object_id = b.constraint_object_id
        FOR XML PATH('')), 1, 1, '') childcolumns
FROM sys.foreign_keys a
INNER JOIN sys.tables b ON a.referenced_object_id = b.object_id
INNER JOIN sys.tables c ON a.parent_object_id = c.object_id;

DECLARE @fkrefs TABLE(referencedtable SYSNAME, parenttable SYSNAME, referencedcolumns varchar(MAX), parentcolumns varchar(MAX))
INSERT @fkrefs
SELECT *, 
    (SELECT TOP 1 b.referencedcolumns
     FROM @fkcolumns b
     WHERE a.referencedtable = b.referencedtable and a.parenttable = b.parenttable),
    STUFF((
        SELECT ';' + b.parentcolumns
        FROM @fkcolumns b
        WHERE a.referencedtable = b.referencedtable and a.parenttable = b.parenttable
        FOR XML PATH('')), 1, 1, '')
FROM (
    SELECT referencedtable, parenttable
    FROM @fkcolumns a
    GROUP BY referencedtable, parenttable
) a;

WITH fks(treelevel, treepath, tablename, referencedcolumns, parentcolumns) AS (
SELECT 1,
    CAST(a.name AS VARCHAR(MAX)),
    a.name,
    CAST('' AS VARCHAR(MAX)),
    CAST('' AS VARCHAR(MAX))
    FROM sys.tables a
    LEFT JOIN @fkrefs c ON a.name = c.parenttable AND c.referencedtable <> c.parenttable
    WHERE c.referencedtable IS NULL
    UNION ALL 
    SELECT treelevel + 1,
        CAST(a.treepath + '_' + b.parenttable AS varchar(MAX)),
        b.parenttable,
        b.referencedcolumns,
        b.parentcolumns
    FROM fks a
    INNER JOIN @fkrefs b ON a.tablename = b.referencedtable
    WHERE treelevel < 10)
SELECT treelevel,
    treepath,
    REPLICATE('|---- ', treelevel) + tablename tablename,
    referencedcolumns,
    parentcolumns
FROM fks
ORDER BY treepath;
