2011-05-02 3 views
10

J'ai une question simple. D'une manière ou d'une autre, j'étais incapable de trouver une réponse définitive.PostgreSQL AVEC une performance RECURSIVE

Quelle est la syntaxe WITH RECURSIVE optimisée dans PostgreSQL? J'entends par là: est-ce simplement un sucre syntaxique pour une série de requêtes non récursives, OU est-ce plus d'une seule déclaration qui malgré sa sémantique complexe a été optimisée dans son ensemble. Une question de suivi - à quel point est-il possible d'optimiser ce type de syntaxe? Bien sûr, des données concrètes sur le sujet sont les bienvenues.

Répondre

4

Mon expérience est que c'est en effet très bien optimisé.

Vérifiez le plan d'exécution pour votre requête générée par EXPLIQUEZ ANALYSER et vous verrez comment il est vraiment « coûteux » (puis comparer que par exemple à une fonction récursive auto écrite)

+1

La raison d'avoir 'WITH RECURSIVE' au niveau du langage est que la base de données sait ce que vous essayez de faire et peut agir en fonction de votre intention. –

+0

@mu Oui, eh bien c'est ce que j'ai compté. Cependant, il n'est pas évident de savoir comment il pourrait par exemple être optimisé simplement en utilisant des indices et même au niveau de la base de données. Certaines opérations sont juste difficiles en général. Suite à la suggestion, je posterai mes résultats dès que j'aurai terminé. – julkiewicz

+0

@julkiewicz Une table de fermeture transitive est en effet un index pour de telles requêtes. Je ne vois aucune raison qu'un type d'index ne puisse pas être proposé utilisant des techniques similaires. – EricS

16

J'ai l'a trouvé optimisé jusqu'à un certain point.

Les différentes sous-requêtes sont réutilisées comme prévu et optimisées individuellement, et Postgres optimise ce dernier comme n'importe quelle autre requête. Mon principal reproche avec cela a à voir avec le fait qu'il n'injectera pas de contraintes dans les CTE quand il le pourrait.

Par exemple:

with recursive 
parents as (
select node.id, 
     node.parent_id 
from nodes as node 
union all 
select node.id, 
     parent.parent_id 
from parents as node 
join nodes as parent on parent.id = node.parent_id 
) 
select parent_id 
from parents 
where id = 2; 

Postgres comprendrait idéalement, dans ce qui précède, que (depuis node.id est retourné comme est) qu'il peut faire:

with recursive 
parents as (
select node.id, 
     node.parent_id 
from nodes as node 
where id = 2 
union all 
select node.id, 
     parent.parent_id 
from parents as node 
join nodes as parent on parent.id = node.parent_id 
) 
select parent_id 
from parents; 

... et utilisez un scan d'index sur la clé primaire. En pratique, cela fonctionnera exactement lorsque le CTE lui dira de faire: tirez récursivement tous les parents pour toutes les lignes, placez le jeu de résultats dans une table temporaire sans nom si nécessaire, puis vérifiez chaque ligne du jeu de résultats un pour id = En d'autres termes, un CTE ne garde pas une trace de l'ensemble table/ligne/colonne "originaire" qu'il retourne. Jusqu'à ce que cela soit optimisé correctement, créer une vue sur une requête récursive est au mieux fou.

Une bonne solution entre-temps est de créer une fonction sql à la place:

create function parents(id int) as returns table (id int) $$ 
    with recursive 
    parents as (
    select node.id, 
      node.parent_id 
    from nodes as node 
    where id = $1 
    union all 
    select node.id, 
      parent.parent_id 
    from parents as node 
    join nodes as parent on parent.id = node.parent_id 
    ) 
    select parent_id 
    from parents; 
$$ language sql stable strict rows 5 cost 1; 

Un autre problème est que vous ne pouvez pas utiliser FOR UPDATE avec CTEs récursif (pour très bien la même raison, en fait).

+0

Étant donné l'âge de cette réponse, êtes-vous au courant de changements récents dans le comportement que vous avez décrit? –

+0

@OliverSalzburg: Je n'ai honnêtement aucune idée, mais il devrait être raisonnablement facile à tester en comparant les plans de requête des requêtes dans la réponse. –