再帰共通テーブル式を使用して、命令グラフ上の望ましいパスを見つけられます。データベース・テーブルの各ローは、命令エッジを表します。各ローは、出発地から目的地まで移動するときの出発地、目的地、およびコストを示します。問題の種類によって、コストは距離であったり、所要時間であったり、または別の基準であることも考えられます。再帰を使用すると、このグラフを介して可能なルートを探索できます。そして、いくつかの可能なルートから目的のルートを選ぶことができます。
たとえば、キッチナー市とペンブローク市の間を車で移動するときの望ましいルートを見つける問題を考えてみます。いくつも可能なルートがあり、それぞれ異なるいくつかの都市を中継点として通ります。目的は、最短ルートをいくつか見つけ、それらを別の適当な選択肢と比較することです。
まず、このグラフのエッジを表すテーブルを定義し、各エッジに対して 1 ローを挿入します。このグラフのすべてのエッジは 2 方向性を持つため、逆方向を表すエッジも挿入する必要があります。このために、最初のロー・セットを選択し、出発地と目的地を入れ替えます。たとえば、一方のローがキッチナー市からトロント市への移動を表し、もう一方のローがトロント市からキッチナー市へ戻る移動を表します。
CREATE TABLE travel ( origin VARCHAR(10), destination VARCHAR(10), distance INT, PRIMARY KEY ( origin, destination ) ); INSERT INTO travel SELECT 'Kitchener', 'Toronto', 105 UNION SELECT 'Kitchener', 'Barrie', 155 UNION SELECT 'North Bay', 'Pembroke', 220 UNION SELECT 'Pembroke', 'Ottawa', 150 UNION SELECT 'Barrie', 'Toronto', 90 UNION SELECT 'Toronto', 'Belleville', 190 UNION SELECT 'Belleville', 'Ottawa', 230 UNION SELECT 'Belleville', 'Pembroke', 230 UNION SELECT 'Barrie', 'Huntsville', 125 UNION SELECT 'Huntsville', 'North Bay', 130 UNION SELECT 'Huntsville', 'Pembroke', 245; INSERT INTO travel -- Insert the return trips SELECT destination, origin, distance FROM travel; |
次に、再帰共通テーブル式を記述します。移動はキッチナー市から始まるため、初期サブクエリは、キッチナー市を起点とする可能なすべてのパスを、その距離とともに選択することから始まります。
再帰サブクエリは、パスを拡張します。各パスに対し、再帰サブクエリは、直前のセグメントの目的地から続くセグメントを追加し、新しいセグメントの距離を加えて、各ルートの現在のトータル・コストを管理します。効率性を考慮して、次の条件のいずれかに該当した場合、ルートは終了します。
パスが出発地に戻る場合。
パスが直前の地点に戻る場合。
パスが最後の目的地に達した場合。
この例では、キッチナー市に戻るパスはなく、全てのパスはペンブローク市に達した場合、終了します。
再帰クエリを使用して環状のグラフを探索する場合は、再帰クエリが適切に終了することを確認することが重要です。この例の場合、前述の条件では不十分です。ルートに 2 つの中継地の間を行ったり来たりする移動が無制限に含まれる可能性があるからです。次の再帰クエリでは、指定したルート内の最大セグメント数を 7 つまでに制限することで、ルートの終わりを保証しています。
このクエリ例のポイントは現実的なルートを選択することであるため、メイン・クエリでは、距離が最短ルート比 50% 増未満のルートのみを選択しています。
WITH RECURSIVE trip ( route, destination, previous, distance, segments ) AS ( SELECT CAST( origin || ', ' || destination AS VARCHAR(256) ), destination, origin, distance, 1 FROM travel WHERE origin = 'Kitchener' UNION ALL SELECT route || ', ' || v.destination, v.destination, -- current endpoint v.origin, -- previous endpoint t.distance + v.distance, -- total distance segments + 1 -- total number of segments FROM trip t JOIN travel v ON t.destination = v.origin WHERE v.destination <> 'Kitchener' -- Don't return to start AND v.destination <> t.previous -- Prevent backtracking AND v.origin <> 'Pembroke' -- Stop at the end AND segments -- TERMINATE RECURSION! < ( SELECT count(*)/2 FROM travel ) ) SELECT route, distance, segments FROM trip WHERE destination = 'Pembroke' AND distance < 1.5 * ( SELECT MIN( distance ) FROM trip WHERE destination = 'Pembroke' ) ORDER BY distance, segments, route; |
この文を前述のデータ・セットに対して実行すると、次のような結果となります。
route | distance | segments |
---|---|---|
Kitchener, Barrie, Huntsville, Pembroke | 525 | 3 |
Kitchener, Toronto, Belleville, Pembroke | 525 | 3 |
Kitchener, Toronto, Barrie, Huntsville, Pembroke | 565 | 4 |
Kitchener, Barrie, Huntsville, North Bay, Pembroke | 630 | 4 |
Kitchener, Barrie, Toronto, Belleville, Pembroke | 665 | 4 |
Kitchener, Toronto, Barrie, Huntsville, North Bay, Pembroke | 670 | 5 |
Kitchener, Toronto, Belleville, Ottawa, Pembroke | 675 | 4 |
Copyright © 2009, iAnywhere Solutions, Inc. - SQL Anywhere 11.0.1 |