2017-10-02 5 views
1

Lors de l'exécution du code ci-dessous:PgSQL: Affectation d'une valeur de colonne à une variable rend paramètre de requête non lié

drop table if exists demo; 
drop table if exists demo_test; 
drop table if exists demo_result; 

create table demo as select md5(v::text) from generate_series(1, 1000000) v; 
create index on demo (md5 text_pattern_ops); 
analyze demo; 

create table demo_test 
    as select left(md5(v::text), 5) || '%' as "patt" from generate_series(2000000, 2000010) v; 

create table demo_result (row text); 

load 'auto_explain'; 
set auto_explain.log_min_duration to 0; 
set auto_explain.log_analyze to true; 
set auto_explain.log_nested_statements to true; 

do $$ 
declare 
    row record; 
pattern text; 
begin 
    for row in select patt from demo_test loop 
     pattern = row.patt; -- <--- CRUCIAL LINE 
     insert into demo_result select * from demo where md5 like pattern; 
    end loop; 
end$$; 

PostgreSQL génère le plan de requête suivante:

2017-10-02 17:03:48 CEST [18038-23] app=psql [email protected] LOG: duration: 0.021 ms plan: 
     Query Text: insert into demo_result select * from demo where md5 like pattern 
     Insert on demo_result (cost=0.42..8.45 rows=100 width=33) (actual time=0.021..0.021 rows=0 loops=1) 
      -> Index Only Scan using demo_md5_idx on demo (cost=0.42..8.45 rows=100 width=33) (actual time=0.018..0.018 rows=1 loops=1) 
       Index Cond: ((md5 ~>=~ '791cc'::text) AND (md5 ~<~ '791cd'::text)) 
       Filter: (md5 ~~ '791cc%'::text) 
       Heap Fetches: 1 

Mais après avoir retiré pattern variable, inline row.patt en where état:

insert into demo_result select * from demo where md5 like row.patt; 

PostgreSQL traite le paramètre comme bind:

2017-10-02 17:03:02 CEST [17901-23] app=psql [email protected] LOG: duration: 89.636 ms plan: 
     Query Text: insert into demo_result select * from demo where md5 like row.patt 
     Insert on demo_result (cost=0.00..20834.00 rows=5000 width=33) (actual time=89.636..89.636 rows=0 loops=1) 
      -> Seq Scan on demo (cost=0.00..20834.00 rows=5000 width=33) (actual time=47.255..89.628 rows=1 loops=1) 
       Filter: (md5 ~~ $4) 
       Rows Removed by Filter: 999999 

Je comprends que le dernier plan emploie balayage séquentiel, parce que PostgreSQL suppose que paramètres de liaison commencent par les caractères génériques.

Ma question est pourquoi les commutateurs d'affectation supplémentaires lient le paramètre on et off?

+0

Question: Obtenez-vous les lignes attendues dans la table avec les deux méthodes? Assurez-vous simplement que nous n'obtenons pas de résultats inattendus en raison d'un conflit avec un mot réservé ou quelque chose d'étrange comme ça. –

+0

Oui, je reçois les mêmes lignes avec les deux versions. –

+0

Avez-vous essayé de convertir les deux en texte pour voir si cela change quelque chose? À ce stade, je ferais des essais et des erreurs pour tenter de discerner la cause des différents plans d'exécution. –

Répondre

1

La différence réside dans les données disponibles pour l'optimiseur au moment où il regarde la requête.

Avec la première requête, le paramètre lié est disponible pour l'optimiseur. Donc il voit qu'il n'y a pas de joker et il sait que l'index peut être utilisé.

insert into demo_result select * from demo where md5 like '791cc%'; 

La deuxième requête n'a aucune idée de ce que le modèle ressemblera donc son pas en mesure de faire l'hypothèse que l'indice est tout bon.

Je suppose que si vous aviez un motif avec un caractère générique principal '% 791cc', vous verriez le même plan de requête utilisé pour les deux approches qu'un seq_scan serait utilisé pour les deux.

+0

Je suis d'accord que c'est comme ça, mais 'row' est aussi une variable. Pourquoi PostgreSQL peut-il optimiser le type 'text', mais pas le type' record'? –

+0

Bon point, j'ai raté cette subtilité. – Gary