2009-08-07 5 views
3

나는 다음과 같이 기본적으로 보이는 테이블이 :하나의 SQL 쿼리에서 "linked list"를 반복합니까?

redirectid가 다른 행에 대한 ID입니다
id | redirectid | data 

. 기본적으로 행이 선택되고 리디렉션 ID가 있으면 해당 리디렉션 데이터를 사용해야합니다. redirectid가 NULL이 될 때까지 여러 번의 리디렉션이있을 수 있습니다. 본질적으로 이러한 리디렉션은 테이블에 연결된 목록을 형성합니다. 내가 알기를 원하는 것은, ID가 주어지면 가능한 모든 리디렉션을 반복하고 "list"의 끝에 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를 선언하고 0으로 초기화하십시오. 그런 다음 더 이상 행을 링크에 추가하지 않을 때까지 다음을 반복하십시오. 그런 다음 링크에 결과가 포함됩니다.

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; 

나는 포스트 그레스 구문에 대한 약간 불안 해요, 당신이 몇 가지 정리를 가질 수 있도록. 어쨌든, 당신은 다음과 같이 함수를 호출 할 수

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를 꽤 빨리 가져야합니다.