2009-08-07 8 views
3

J'ai une table qui ressemble fondamentalement ceci:Itérer via "liste liée" dans une requête SQL?

id | redirectid | data 

où le redirectid est un identifiant à une autre ligne. Fondamentalement, si une ligne est sélectionnée et qu'elle a un redirectid, les données redirectid doivent être utilisées à sa place. Il peut y avoir plusieurs redirections jusqu'à ce que redirectid soit NULL. Essentiellement, ces redirections forment une liste liée dans la table. Ce que je voudrais savoir est, donné un id, est-il possible de mettre en place une requête sql qui parcourra toutes les redirections possibles et retourner l'id à la fin de la "liste"? Ceci utilise Postgresql 8.3 et j'aimerais faire tout ce qui est en requête sql si possible (plutôt que de répéter dans mon code).

Répondre

2

Est-ce que postgresql prend en charge les requêtes récursives qui utilisent les clauses WITH? Si c'est le cas, quelque chose comme ça pourrait fonctionner. (Si vous voulez une réponse testée, fournir des instructions CREATE TABLE et INSERT dans votre question, ainsi que les résultats dont vous avez besoin pour les données d'échantillon dans les INSERTs.)

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; 

Remarques complémentaires:

:(Vous pouvez implémenter la récursion vous-même en fonction de l'expression WITH Je ne connais pas la syntaxe postgresql pour la programmation séquentielle, donc c'est un peu pseudo:

Insérez le résultat de cette requête dans une nouvelle table appelée Liens:

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 

Déclare également un entier :: depth et l'initialise à zéro. Puis répétez les opérations suivantes jusqu'à ce qu'il n'ajoute plus de lignes aux liens. Les liens contiendront alors votre résultat.

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; 

Je pense que ce sera mieux que n'importe quelle solution de curseur. En fait, je ne peux pas vraiment penser à la façon dont les curseurs seraient utiles pour ce problème. Notez que cela ne se terminera pas s'il y a des cycles (redirections qui sont finalement circulaires).

+0

Malheureusement il semble que le support récursif n'a pas été ajouté jusqu'à 8.4 –

+0

Voir mes remarques supplémentaires dans la réponse. –

1

Je dirais que vous devez créer un user-defined function dans cette veine:

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; 

Je suis un peu fragile sur la syntaxe Postgres, vous pouvez avoir un nettoyage à faire. Quoi qu'il en soit, vous pouvez appeler votre fonction comme ceci:

select id, FindLastId(id) as EndId from table 

Sur une table comme ceci:

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

Cela renverra:

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

Notez que ce sera nettement lent, mais il devrait vous obtenir l'ID assez rapidement pour un petit jeu de résultats sur une table bien indexée.

Questions connexes