1

Schéma:Pourquoi PostgreSQL n'utilise pas l'index correctement?

create table records(
    id   varchar, 
    updated_at bigint 
); 
create index index1 on records (updated_at, id); 

Recherche. Il itère sur les enregistrements récemment mis à jour. Récupère 10 enregistrements, se souvient du dernier, puis récupère les 10 suivants et ainsi de suite.

select * from objects 
where updated_at > '1' or (updated_at = '1' and id > 'some-id') 
order by updated_at, id 
limit 10; 

Il utilise l'index, mais cela ne l'utilise à bon escient et applique également le filtre et traite des tonnes de dossiers, voir Rows Removed by Filter: 31575 dans l'explication de la requête ci-dessous.

La chose étrange est que si vous supprimez or et laissez l'une des conditions gauche ou à droite - il fonctionne bien pour les deux. Mais il semble que si vous ne pouvez pas comprendre comment appliquer correctement l'index si les deux conditions sont utilisées simultanément avec or.

Limit (cost=0.42..19.03 rows=20 width=1336) (actual time=542.475..542.501 rows=20 loops=1) 
    -> Index Scan using index1 on records (cost=0.42..426791.29 rows=458760 width=1336) (actual time=542.473..542.494 rows=20 loops=1) 
     Filter: ((updated_at > '1'::bigint) OR ((updated_at = '1'::bigint) AND ((id)::text > 'some-id'::text))) 
     Rows Removed by Filter: 31575 
Planning time: 0.180 ms 
Execution time: 542.532 ms 
(6 rows) 

la version Postgres est 9.6

+0

'... où updated_at> '1' ...' Vous ne devriez pas citation littéraux entiers. – wildplasser

+0

@wildplasser Je l'ai essayé sans guillemets, même chose. –

+0

'width = 1336' C'est une table * très * large, – wildplasser

Répondre

2

Je voudrais essayer cela comme deux requêtes distinctes, combinant leurs résultats comme celui-ci:

select * 
from 
    (
    select * 
    from  objects 
    where updated_at > 1 
    order by updated_at, id 
    limit 10 
    union all 
    select * 
    from  objects 
    where updated_at = 1 
     and id > 'some-id' 
    order by updated_at, id 
    limit 10 
) t 
order by updated_at, id 
limit 10 

Je pense que les deux requêtes seraient chacun optimiser assez bien et courir les deux serait plus efficace que l'actuel.

Je voudrais également rendre ces colonnes NOT NULL si possible.

+0

Oui, j'ai aussi pensé à ça. Mais je pensais que PostgreSQL est assez intelligent et peut-être qu'il y a une erreur dans mon code ... –

+0

Oui, il a résolu le problème grâce à. Étrange ... Je m'attendais à mieux de PostgreSQL ... –

2

Il y a une optimisation des appels à l'index faite par PostgreSQL.

Par exemple, étant donné un index sur (a, b, c) et une condition de requête d'où une = 5 et b> = 42 et c < 77, l'indice devrait être analysé à partir de la première entrée avec a = 5 et b = 42 jusqu'à la dernière entrée avec un = 5. Les entrées d'index avec c> = 77 seraient ignorées, mais elles devraient encore être analysées. Cet indice pourrait en principe être utilisé pour les requêtes qui ont des contraintes sur b et/ou c sans contrainte sur - mais l'index entier devrait être balayé, donc dans la plupart des cas, le planificateur préférerait une table séquentielle SUR.BAL en utilisant l'index.

https://www.postgresql.org/docs/9.6/static/indexes-multicolumn.html