質問:
次の(有向)グラフがあります。
そして、このテーブル:
CREATE TABLE [dbo].[T_Hops](
[UID] [uniqueidentifier] NULL,
[From] [nvarchar](1000) NULL,
[To] [nvarchar](1000) NULL,
[Distance] [decimal](18, 5) NULL
) ON [PRIMARY]
GO
そして、このコンテンツ:
INSERT INTO [dbo].[T_Hops] ([UID] ,[From] ,[To] ,[Distance]) VALUES (newid() ,'A' ,'E' ,10.00000 );
INSERT INTO [dbo].[T_Hops] ([UID] ,[From] ,[To] ,[Distance]) VALUES (newid() ,'E' ,'D' ,20.00000 );
INSERT INTO [dbo].[T_Hops] ([UID] ,[From] ,[To] ,[Distance]) VALUES (newid() ,'A' ,'B' ,5.00000 );
INSERT INTO [dbo].[T_Hops] ([UID] ,[From] ,[To] ,[Distance]) VALUES (newid() ,'B' ,'C' ,10.00000 );
INSERT INTO [dbo].[T_Hops] ([UID] ,[From] ,[To] ,[Distance]) VALUES (newid() ,'C' ,'D' ,5.00000 );
INSERT INTO [dbo].[T_Hops] ([UID] ,[From] ,[To] ,[Distance]) VALUES (newid() ,'A' ,'F' ,2.00000 );
INSERT INTO [dbo].[T_Hops] ([UID] ,[From] ,[To] ,[Distance]) VALUES (newid() ,'F' ,'G' ,6.00000 );
INSERT INTO [dbo].[T_Hops] ([UID] ,[From] ,[To] ,[Distance]) VALUES (newid() ,'G' ,'H' ,3.00000 );
INSERT INTO [dbo].[T_Hops] ([UID] ,[From] ,[To] ,[Distance]) VALUES (newid() ,'H' ,'D' ,1.00000 );
これで、ポイント x からポイント y への最適な接続を次のようにクエリできます。
WITH AllRoutes
(
[UID]
,[FROM]
,[To]
,[Distance]
,[Path]
,[Hops]
)
AS
(
SELECT
[UID]
,[FROM]
,[To]
,[Distance]
,CAST(([dbo].[T_Hops].[FROM] + [dbo].[T_Hops].[To]) AS varchar(MAX)) AS [Path]
,1 AS [Hops]
FROM [dbo].[T_Hops]
WHERE [FROM] = 'A'
UNION ALL
SELECT
[dbo].[T_Hops].[UID]
--,[dbo].[T_Hops].[FROM]
,Parent.[FROM]
,[dbo].[T_Hops].[To]
,CAST((Parent.[Distance] + [dbo].[T_Hops].[Distance]) AS [decimal](18, 5)) AS distance
,CAST((Parent.[Path] + '/' + [dbo].[T_Hops].[FROM] + [dbo].[T_Hops].[To]) AS varchar(MAX)) AS [Path]
,(Parent.[Hops] + 1) AS [Hops]
FROM [dbo].[T_Hops]
INNER JOIN AllRoutes AS Parent
ON Parent.[To] = [dbo].[T_Hops].[FROM]
)
SELECT TOP 100 PERCENT * FROM AllRoutes
/*
WHERE [FROM] = 'A'
AND [To] = 'D'
AND CHARINDEX('F', [Path]) != 0 -- via F
ORDER BY Hops, Distance ASC
*/
GO
今、無向グラフを作成したいので、たとえば、D から A へのパスも取得できます。
私は最も単純な変更から始めて、HD の逆方向を広告するだけです。
INSERT INTO [dbo].[T_Hops]
([UID]
,[From]
,[To]
,[Distance])
VALUES
(newid() --<UID, uniqueidentifier,>
,'D' --<From, nvarchar(1000),>
,'H' --<To, nvarchar(1000),>
,1 --<Distance, decimal(18,5),>
)
GO
さて、予想通り、私のクエリは例外をスローします:
無限再帰 / 最大再帰レベル (100) を超えました
可能な接続の数が無限になったためです。
Oracle では、ツリーの代わりに「事前に接続」を使用して同じことを行います。そして、循環問題 (無限再帰) が発生する可能性がある場合は、NOCYCLE を CONNECT BY PRIOR に追加して、「CONNECT BY NOCYCLE PRIOR」にします。
MS-SQL では、以下を追加してその動作を修正しました。
AND Parent.[Path] NOT LIKE '%' + [dbo].[T_Hops].[FROM] + '/%'
基本的に NOCYCLE をエミュレートします。
ただし、LIKE は基本的に strstr (またはより悪い strcasestr) であるため、親要素の配列をチェックするよりも非常に遅く、パフォーマンスが非常に心配です。
あくまでもこれは一例であり、基本的には全国のデータを追加するつもりです。したがって、最終結果は非常に遅くなる可能性があります。
他の誰かが MS SQL で NOCYCLE を置き換える方法のより良い (= より速い) 方法を持っていますか?
それとも、これは、Oracleに切り替える以外に選択肢がないポイントですか(これを許容できる速度で実行するため)?
注: 一時テーブル (大量のデータ) ソリューションは、十分な RAM がない場合 (絶対確実) に一時テーブルがハードディスクにスワップされるため、遅くなります。
関数とテーブル値関数を使用するソリューションについても同様です。