【问题标题】:Iterate through "linked list" in one SQL query?在一个 SQL 查询中遍历“链表”?
【发布时间】:2009-08-07 20:00:37
【问题描述】:

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

id | redirectid | data

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

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

【问题讨论】:

    标签: sql postgresql iteration


    【解决方案1】:

    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
    

    还声明一个整数 ::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;
    

    我认为这比任何游标解决方案都要好。事实上,我真的想不出游标对这个问题有什么用处。

    请注意,如果有任何循环(最终是循环的重定向),这不会终止。

    【讨论】:

    • 不幸的是,似乎直到 8.4 才添加递归支持
    【解决方案2】:

    我想说你应该按照这种方式创建一个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。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-01-15
      • 2013-08-24
      • 1970-01-01
      • 1970-01-01
      • 2014-08-28
      • 2021-08-04
      • 2014-06-10
      相关资源
      最近更新 更多