web-dev-qa-db-fra.com

Poussez la condition postgres lors de l'utilisation de conditions de plage (> =, <= ou entre)

Je tiens à mieux comprendre les circonstances dans lesquelles Postgres Requête le bourreau pousse les conditions dans les composants de la jointure. Je serais particulièrement reconnaissant de références aux pièces de code source responsables de cela.

Disons que vous avez une vue dont le corps est une jointure entre deux tables sur un champ de date, comme celui-ci (il s'agit d'un exemple de démontage trivial sans interprétation de la vie réelle):

Schéma (PostgreSQL V13)

create table measures (mdate date, measurement numeric);
create index measures_mdate on measures(mdate);

create view name_matches_surname as 
   select m1.*, m2.measurement as the_other_measurement 
   from measures m1 left join measures m2 on (m1.mdate = m2.mdate);
            
insert into measures values ('2021-05-11',1.0), ('2021-05-11',2.0);

Si je sélectionne à partir de cette vue avec une condition d'égalité sur le champ Date, la condition serait enfoncée dans les deux côtés de la jointure, comme on pouvait le voir ici:

Query n ° 1: (= 'Maintenant') est enfoncé

explain select * from name_matches_surname where mdate='now'::date;
| QUERY PLAN                                                                              |
| --------------------------------------------------------------------------------------- |
| Nested Loop Left Join  (cost=8.40..27.89 rows=36 width=68)                              |
|   Join Filter: (m1.mdate = m2.mdate)                                                    |
|   ->  Bitmap Heap Scan on measures m1  (cost=4.20..13.67 rows=6 width=36)               |
|         Recheck Cond: (mdate = '2021-05-12'::date)                                      |
|         ->  Bitmap Index Scan on measures_mdate  (cost=0.00..4.20 rows=6 width=0)       |
|               Index Cond: (mdate = '2021-05-12'::date)                                  |
|   ->  Materialize  (cost=4.20..13.70 rows=6 width=36)                                   |
|         ->  Bitmap Heap Scan on measures m2  (cost=4.20..13.67 rows=6 width=36)         |
|               Recheck Cond: (mdate = '2021-05-12'::date)                                |
|               ->  Bitmap Index Scan on measures_mdate  (cost=0.00..4.20 rows=6 width=0) |
|                     Index Cond: (mdate = '2021-05-12'::date)                            |

Comme vous pouvez le constater, les deux côtés de la jointure sont des analyses d'index avec condition (= 'maintenant') utilisée pour accéder à l'index. Jusqu'ici tout va bien. Permet d'essayer la condition de gamme (entre) à la place:

Query n ° 2: entre est enfoncé dans un côté de la jointure uniquement

explain select * from name_matches_surname 
  where mdate between 'now'::date-5 and 'now'::date;
| QUERY PLAN                                                                                        |
| ------------------------------------------------------------------------------------------------- |
| Hash Right Join  (cost=13.77..41.61 rows=38 width=68)                                             |
|   Hash Cond: (m2.mdate = m1.mdate)                                                                |
|   ->  Seq Scan on measures m2  (cost=0.00..22.70 rows=1270 width=36)                              |
|   ->  Hash  (cost=13.70..13.70 rows=6 width=36)                                                   |
|         ->  Bitmap Heap Scan on measures m1  (cost=4.21..13.70 rows=6 width=36)                   |
|               Recheck Cond: ((mdate >= '2021-05-07'::date) AND (mdate <= '2021-05-12'::date))     |
|               ->  Bitmap Index Scan on measures_mdate  (cost=0.00..4.21 rows=6 width=0)           |
|                     Index Cond: ((mdate >= '2021-05-07'::date) AND (mdate <= '2021-05-12'::date)) |

Comme vous pouvez le constater, entre celui-ci a été poussé dans le côté gauche de la jointure seulement et pour le côté droit, nous avons SEQ Scan. Vous pouvez facilement vérifier que le type de jointure (interne ou gauche) ou la taille de la table n'a aucune incidence sur le résultat de la poussée - la condition ne sera pas enfoncée dans le côté droit de la jointure.

Pour autant que je sache, ce comportement est cohérent pour toutes les versions entre Postgres 9.5 et 13.

Quelle est la raison pour ça? Est-il possible de réécrire la vue de manière différente de sorte que les entre soient repoussés "également bien", comme (=)? Existe-t-il des références à la documentation ou au code source qui explique les stratégies de poussée en profondeur?


Vue sur db violon

2
ADEpt

Avec =, l'optimiseur déduit que si m1.mdate = m2.mdate et m1.mdate = 'constant', la même chose doit tenir pour m2.mdate. Aucune telle inférence n'a lieu pour les opérateurs d'inégalité.

1
Laurenz Albe