sys.foreign_keysの階層型クエリは、探しているものにかなり近づくはずです。
WITH cte AS (
-- find tables that are parents, but are not children themselves
SELECT [fk].[referenced_object_id] AS [child_id],
NULL AS [parent_id],
CAST(CONCAT('/', [fk].[referenced_object_id], '/') AS VARCHAR(MAX)) AS h,
1 AS l
FROM sys.[foreign_keys] AS [fk]
WHERE [fk].[referenced_object_id] NOT IN (
SELECT [parent_object_id]
FROM sys.[foreign_keys]
)
UNION ALL
SELECT child.[parent_object_id],
[child].[referenced_object_id] AS [parent_id],
CAST(CONCAT(parent.[h], child.[parent_object_id], '/') AS VARCHAR(MAX)) AS [h],
parent.l + 1 AS l
FROM cte AS [parent]
JOIN sys.[foreign_keys] AS [child]
ON [parent].[child_id] = child.[referenced_object_id]
),
hier AS (
SELECT DISTINCT
OBJECT_NAME([cte].[child_id]) AS [child],
object_name([cte].[parent_id]) AS [parent],
h,
--CAST([cte].[h] AS HIERARCHYID) AS h
l
FROM cte
)
SELECT [hier].[child] ,
[hier].[parent] ,
[hier].[h]--.ToString()
FROM [hier]
ORDER BY
l, h -- breadth-first search
--h, l -- depth-first search
--h.GetLevel(), h -- breadth-first search; hierarchyid
--h, h.GetLevel() -- depth-first search; hierarchyid
私は2つのorder by句を含めることに注意してください。それぞれに用途があります。外部キーの切断グラフ(a→b→c)、(d→e→f)があるとします。最初のorder by節を使用すると、a、d、b、e、c、fの順番で行が返されます。つまり、最上位の要素のすべてが最初に、その後に2つの要素が続くなどです.2番目のorder by節は、a、b、c、d、e、fの順にそれらを返します(またはd、e 、f、a、b、c; aとdのオブジェクトIDによって異なります)。ここでのアイデアは、切断されたグラフを次のグラフに移動する前に完全に使い果たしたということです。
私は、上記で自己参照の外部キーを考慮していないと確信しています。それがあなたにとって重要なのであれば、私はそれらを別々の行動として扱います(つまり、最初にそれらを完全に埋め込み、上記を使用して非自己参照関係を見つける)。
また、hierarchyidソリューションを作成するためのコメントも2つ残しました。 hier
cteでは、hの代わりにh
のcastingidをhierarchyidに使用し、それを利用するorder by節を使用します。それのどれもが必要ではありませんが、hierarchyidへの最初の良い露出かもしれません。
これを達成するにはネットワークの問題を解決する必要がありますが、それはそれほど簡単ではないと思います。 –