再帰的なSQLクエリを作成することは可能ですか?
-
09-06-2019 - |
質問
これに似たテーブルがあります。
CREATE TABLE example (
id integer primary key,
name char(200),
parentid integer,
value integer);
parentid フィールドを使用すると、データをツリー構造に配置できます。
さて、ここで私が解決できない部分があります。親 ID が与えられた場合、その親 ID の下にあるすべての値フィールドを合計し、ツリーのブランチを再帰する SQL ステートメントを作成することは可能ですか?
アップデート: 私は posgreSQL を使用しているため、高度な MS-SQL 機能は利用できません。いずれにせよ、これは一般的な SQL の質問として扱われることを望みます。
ところで、質問してから 15 分以内に 6 件の回答が得られたことに非常に感銘を受けました。スタックオーバーフローに行きましょう!
解決
PostgreSQL で必要なことを行うには、いくつかの方法があります。
モジュールをインストールできる場合は、tablefunc contrib を確認してください。これには、ツリーの走査を処理する connectby() 関数があります。 http://www.postgresql.org/docs/8.3/interactive/tablefunc.html
ltree contrib もチェックしてください。テーブルを適応させて使用できます。 http://www.postgresql.org/docs/8.3/interactive/ltree.html
または、PL/PGSQL 関数を使用して自分でツリーを走査することもできます。
このようなもの:
create or replace function example_subtree (integer)
returns setof example as
'declare results record;
child record;
begin
select into results * from example where parent_id = $1;
if found then
return next results;
for child in select id from example
where parent_id = $1
loop
for temp in select * from example_subtree(child.id)
loop
return next temp;
end loop;
end loop;
end if;
return null;
end;' language 'plpgsql';
select sum(value) as value_sum
from example_subtree(1234);
他のヒント
共通テーブル式を使用したスクリプトの例を次に示します。
with recursive sumthis(id, val) as (
select id, value
from example
where id = :selectedid
union all
select C.id, C.value
from sumthis P
inner join example C on P.id = C.parentid
)
select sum(val) from sumthis
上記のスクリプトは、という「仮想」テーブルを作成します。 sumthis
列があるもの id
そして val
. 。これは、2 つの選択をマージした結果として定義されます。 union all
.
初め select
ルートを取得します (where id = :selectedid
).
2番 select
返すものがなくなるまで、前の結果の子を繰り返し追跡します。
最終結果は通常のテーブルと同様に処理できます。この場合、val 列が合計されます。
バージョン 8.4 以降、PostgreSQL には 再帰クエリのサポート SQL 標準を使用した共通テーブル式の場合 WITH
構文。
あらゆる ANSI で動作するポータブルなソリューションが必要な場合 SQL-92 RDBMS の場合は、テーブルに新しい列を追加する必要があります。
ジョー・セルコはこの作品の原作者です。 ネストされたセット SQL で階層を保存するアプローチ。Googleできます 「ネストされたセット」階層 背景をより深く理解するために。
または、parentid の名前を次のように変更することもできます 左端 そして追加します 右ID.
以下は、ネストされたセットを要約する私の試みですが、私は Joe Celko ではないので、ひどく不十分になるでしょう。SQL はセットベースの言語であり、隣接モデル (親 ID を保存する) はセットベースの階層表現ではありません。したがって、隣接関係スキーマをクエリするための純粋なセットベースの方法はありません。
しかし, 、主要なプラットフォームのほとんどは、この正確な問題に対処するために近年拡張機能を導入しました。したがって、誰かが Postgres 固有のソリューションについて返信した場合は、ぜひそれを使用してください。
再帰クエリを作成する標準的な方法 SQL
再帰的です CTE
. PostgreSQL
それ以来彼らをサポートしています 8.4
.
以前のバージョンでは、再帰的にセットを返す関数を作成できました。
CREATE FUNCTION fn_hierarchy (parent INT)
RETURNS SETOF example
AS
$$
SELECT example
FROM example
WHERE id = $1
UNION ALL
SELECT fn_hierarchy(id)
FROM example
WHERE parentid = $1
$$
LANGUAGE 'sql';
SELECT *
FROM fn_hierarchy(1)
この記事を参照してください。
SQL Server 2005 を使用している場合は、共通テーブル式を使用してこれを行う非常に優れた方法があります。
これにより、一時テーブルを作成するという面倒な作業がすべてなくなり、基本的に WITH と UNION だけですべての作業を行うことができます。
ここに良いチュートリアルがあります:
http://searchwindevelopment.techtarget.com/tip/0,289483,sid8_gci1278207,00.html
使う 共通テーブル式.
これが SQL Server 2005 以降のみであることを示したい場合があります。 デール・ラガン
ここに記事があります 共通のテーブル式を使用しない SqlTeam による再帰の場合。
次のコードはコンパイルされ、テストでは問題なく実行されます。
create or replace function subtree (bigint) returns setof example as $$ declare results record; entry record; recs record; begin select into results * from example where parent = $1; if found then for entry in select child from example where parent = $1 and child parent loop for recs in select * from subtree(entry.child) loop return next recs; end loop; end loop; end if; return next results; end; $$ language 'plpgsql';
私の場合、ノードが自分自身を指しているため、「子 <> 親」という条件が必要です。
楽しむ :)
Oracleには「START WITH」と「CONNECT BY」があります。
select
lpad(' ',2*(level-1)) || to_char(child) s
from
test_connect_by
start with parent is null
connect by prior child = parent;
簡単な余談ですが、質問には非常によく答えられていますが、これを次のように扱うと注意が必要です。
一般的なSQLの質問
SQL'99 では仕様内で線形再帰が許可されているため (標準を完全に実装している RDBMS はないと思いますが)、SQL の実装は非常に簡単です。 WITH RECURSIVE
声明。したがって、理論的な観点からは、これを今すぐ行うことができます。
どの例も私にとってはうまく機能しなかったので、次のように修正しました。
declare results record; entry record; recs record; begin for results in select * from project where pid = $1 loop return next results; for recs in select * from project_subtree(results.id) loop return next recs; end loop; end loop; return; end;
これは SQL Server ですか?結果をループして結合する TSQL ストアド プロシージャを作成できないでしょうか?
SQL のみでこれを行う方法があるかどうかにも興味があります。地理データベースの授業で覚えている限りでは、あるはずです。
SQL 2008ではもっと簡単だと思います 階層ID
階層だけでなく任意のグラフを保存する必要がある場合は、Postgres を脇に置いて、次のようなグラフ データベースを試すことができます。 アレグログラフ:
グラフ データベース内のすべてはトリプル (ソース ノード、エッジ、ターゲット ノード) として保存され、SQL のような言語を使用してグラフ構造を操作し、クエリを実行するための最高級のサポートを提供します。
Hibernate や Django ORM などとはうまく統合できませんが、(Nested Set モデルのような階層だけでなく) グラフ構造について真剣に考えている場合は、チェックしてみてください。
また、Oracle がついに最新製品に実際のグラフのサポートを追加したと私は信じていますが、これほど時間がかかったのには驚き、多くの問題がこのモデルから恩恵を受ける可能性があります。