I wrote a query to find out primary key - foreign key relationship between all the tables in a database. This works only if we have foreign keys present physically.
Please run this query to understand my problem clearly.
WITH cte
AS
(
SELECT
fk.create_date
, fk.modify_date
, fkc.constraint_object_id AS ConstraintId
, OBJECT_NAME(fkc.constraint_object_id) AS ConstraintName
--, fkc.referenced_object_id AS PrimaryKeyTableId
, OBJECT_NAME(fkc.referenced_object_id) AS PrimaryKeyTableName
--, fkc.referenced_column_id AS PrimaryKeyColumnId
, rc.name AS PrimaryKeyColumnName
--, fk.parent_object_id AS ForeignKeyTableId
, OBJECT_NAME(fk.parent_object_id) AS ForeignKeyTableName
--, fkc.parent_column_id AS ForeignKeyColumnId
, lc.name AS ForeignKeyColumnName
FROM sys.foreign_key_columns fkc
INNER JOIN sys.columns rc
ON rc.OBJECT_ID = fkc.referenced_object_id
AND fkc.referenced_column_id = rc.column_id
INNER JOIN sys.foreign_keys fk
ON fk.OBJECT_ID = fkc.constraint_object_id
INNER JOIN sys.columns lc
ON lc.OBJECT_ID = fk.parent_object_id
AND fkc.parent_column_id = lc.column_id
)
, cte2(create_date, modify_date, ConstraintName
, PrimaryKeyTableName, PrimaryKeyColumnName
, ForeignKeyTableName, ForeignKeyColumnName
, Hops, path ) AS
(
SELECT
create_date, modify_date, ConstraintName
, PrimaryKeyTableName, PrimaryKeyColumnName
, ForeignKeyTableName, ForeignKeyColumnName
, 1 , CAST(QUOTENAME(PrimaryKeyTableName + '.' + PrimaryKeyColumnName) AS VARCHAR(4000))
FROM cte
UNION ALL
SELECT
cte.create_date, cte.modify_date, cte.ConstraintName
, cte.PrimaryKeyTableName, cte.PrimaryKeyColumnName
, cte.ForeignKeyTableName, cte.ForeignKeyColumnName
, cte2.Hops +1, CAST(cte2.path + '-> ' +QUOTENAME(cte.PrimaryKeyTableName+ '.' + cte.PrimaryKeyColumnName) AS VARCHAR(4000))
FROM cte2 INNER JOIN cte ON cte2.ForeignKeyTableName = cte.PrimaryKeyTableName
AND cte2.ForeignKeyColumnName != cte.PrimaryKeyColumnName
)
SELECT
ConstraintName
, PrimaryKeyTableName, PrimaryKeyColumnName
, ForeignKeyTableName, ForeignKeyColumnName
, Hops, path + '-> ' + QUOTENAME(ForeignKeyTableName + '.' + ForeignKeyColumnName) AS Path
FROM cte2
Above query works smoothly unless we have composite primary key present in database.
Let's say I have a table
- Method(MethodId, ....)
- Parameter(ParameterId, ...)
- ParameterMethodMap(ParameterId, MethodId) --Composite primary key
- Test(TestId, ParameterId, MethodId....) --Composite primary key being used as foreign key
- Sample(SampleId, TestId....)
So current query do not generate path considering Composite Primary key scenario.
I want to generate path like.
[Method.MethodId] -> [ParameterMethodMap.MethodId, ParameterMethodMap.ParameterId] -> [Test.TestId] -> [Sample.SampleId]
This is somehow I am thinking to merge composite primary key. How can I do this?
Well, in general because you cannot use aggregate functions in recursive part of CTE, you should move column concatenation part to another CTE. You will have: