在一个SQL查询中迭代“链表”?

时间:2009-08-07 20:00:38

标签: sql postgresql iteration

我有一张看起来基本像这样的表:

id | redirectid | data

其中redirectid是另一行的id。基本上如果选择了一行,并且它有一个重定向,那么应该在它的位置使用重定向数据。可能存在多个重定向,直到redirectid为NULL。基本上这些重定向在表格中形成一个链表。我想知道的是,给定一个id,是否可以设置一个sql查询来迭代所有可能的重定向并返回“list”末尾的id?

这是使用Postgresql 8.3,如果可能的话,我想在sql查询中做一切(而不是在我的代码中迭代)。

2 个答案:

答案 0 :(得分:2)

postgresql是否支持使用WITH子句的递归查询?如果是这样,这样的事情可能会奏效。 (如果您需要经过测试的答案,请在您的问题中提供一些CREATE TABLE和INSERT语句,以及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并将其初始化为零。然后重复以下操作,直到它不再向Links添加行。然后链接将包含您的结果。

  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;

我认为这比任何游标解决方案都要好。事实上,我根本无法想到游标将如何对这个问题有用。

请注意,如果存在任何循环(重定向最终为循环),则不会终止此操作。

答案 1 :(得分: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。

相关问题