2009-08-07 9 views
3

私はこのように基本的に見えるテーブルを有する:1つのSQLクエリで「リンクされたリスト」を繰り返しますか?

redirectidが別の行に、IDである
id | redirectid | data 

。基本的に、行が選択されていて、それがredirectidを持っているなら、redirectidデータをその場所で使うべきです。 redirectidがNULLになるまで、複数のリダイレクトが存在する可能性があります。基本的に、これらのリダイレクトはテーブルにリンクリストを形成します。私が知りたいのは、IDを与えられた場合、すべての可能なリダイレクトを繰り返し、 "リスト"の最後にIDを返すSQLクエリを設定できるかどうかです。

これはPostgreSQL 8.3を使用しています。可能であれば、私のコードでは反復するのではなく、すべてSQL問合せを実行したいと思います。

答えて

2

postgresqlはWITH句を使用する再帰的クエリをサポートしていますか?もしそうなら、このようなものが働くかもしれません。 (あなたがテストした答えをしたい場合は、いくつかは、あなたがのINSERTのサンプルデータのために必要な結果とともに、あなたの質問にTABLEとINSERT文のCREATE提供しています。)

with Links(id,link,data) as (
    select 
    id, redirectid, data 
    from T 
    where redirectid is null 
    union all 
    select 
    id, redirectid, null 
    from T 
    where redirectid is not null 
    union all 
    select 
    Links.id, 
    T.redirectid, 
    case when T.redirectid is null then T.data else null end 
    from T 
    join Links 
    on Links.link = T.id 
) 
    select id, data 
    from Links 
    where data is not null; 

追加発言:

私はシーケンシャルプログラミングのためのpostgresql構文を知らないので、これは少し擬似です:

このクエリの結果をLinks:

という新しいテーブルに挿入してください
select 
    id, redirectid as link, data, 0 as depth 
    from T 
    where redirectid is null 
    union all 
    select 
    id, redirectid, null, 0 
    from T 
    where redirectid is not null 

また、integer :: depthを宣言してゼロに初期化します。次に、リンクに行が追加されなくなるまで、次の操作を繰り返します。リンクに結果が表示されます。

increment ::depth; 
    insert into Links 
    select 
    Links.id, 
    T.redirectid, 
    case when T.redirectid is null then T.data else null end, 
    depth + 1 
    from T join Links 
    on Links.link = T.id 
    where depth = ::depth-1; 
end; 

これはどのカーソルソリューションよりも優れていると思います。実のところ、この問題にカーソルがどのように役立つのか全く考えることはできません。

サイクル(リダイレクトは最終的には円形)がある場合、これは終了しないことに注意してください。

+0

残念ながら、再帰的サポートは8.4まで追加されていないようです。 –

+0

答えに私の追加のコメントを見てください。 –

1

私はあなたがこの調子でuser-defined functionを作成する必要が言うと思います:

create function FindLastId (ID as integer) returns integer as $$ 
    declare newid integer; 
    declare primaryid integer; 
    declare continue boolean; 
    begin 
     set continue = true; 
     set primaryid = $1; 
     while (continue) 
      select into newid redirectid from table where id = :primaryid; 

      if newid is null then 
       set continue = false; 
      else 
       set primaryid = :newid; 
      end if; 
     end loop; 

     return primaryid; 
    end; 
    $$ language pgplsql; 

私はPostgresの構文上のビット不安定ですので、あなたが行うには、いくつかのクリーンアップを有することができます。これが返されます

id  redirectid data 
1   3   ab 
2  null   cd 
3   2   ef 
4   1   gh 
5  null   ij 

:そうのようにテーブルの上に

select id, FindLastId(id) as EndId from table 

:とにかく、あなたはそのようにように、あなたの関数を呼び出すことができます

id EndId 
1  2 
2  2 
3  2 
4  2 
5  5 

をこれが著しく遅くなることに注意してください、しかし、それはあなたにIDを取得する必要がありますかなり短いインデックスのテーブルに設定された小さな結果のために。