2009-08-07 110 views
3

我有一個表,基本上是這樣的:在一個SQL查詢中遍歷「鏈接列表」?

id | redirectid | data 

其中redirectid是一個id到另一行。基本上,如果選擇了一行,並且它有重定向,那麼重定向數據應該用在它的位置上。可能有多個重定向,直到redirectid爲NULL。實質上,這些重定向在表格中形成鏈接列表。我想知道的是,給定一個id,是否可以設置一個sql查詢來迭代所有可能的重定向,並返回「列表」末尾的id?

這是使用Postgresql 8.3,我想盡可能在​​sql查詢中做所有事情(而不是在我的代碼中迭代)。

回答

2

postgresql是否支持使用WITH子句的遞歸查詢?如果是這樣,這樣的事情可能會起作用。 (如果你想有一個測試的答案,提供一些CREATE TABLE和INSERT語句在你的問題,你需要在插入樣本數據的結果一起。)

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; 

補充說明:

:(你可以基於WITH表達式自己實現遞歸,我不知道用於順序編程的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 – 2009-08-07 20:31:00

+0

才被添加。請參閱我在答案中的其他評論。 – 2009-08-09 01:40:49

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的語法有點不穩,所以你可能有一些清理工作要做。無論如何,你可以調用你的函數,像這樣:

select id, FindLastId(id) as EndId from table 

在桌子上像這樣:

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

這將返回:

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

注意,這將是明顯地慢,但它應該很快爲您在索引良好的表上爲一個小結果集提供ID。