2011-12-15 1 views
5

J'ai le tableau suivant:Postgres - Est-ce la bonne façon de créer un index partiel sur une colonne booléenne?

CREATE TABLE recipemetadata 
(
    --Lots of columns 
    diet_glutenfree boolean NOT NULL, 
); 

La plupart de chaque ligne sera mis à FALSE à moins que quelqu'un arrive avec une nouvelle alimentation sans gluten fou qui balaie le pays.

Je dois pouvoir interroger très rapidement les lignes où cette valeur est vraie. J'ai créé l'index:

CREATE INDEX IDX_RecipeMetadata_GlutenFree ON RecipeMetadata(diet_glutenfree) WHERE diet_glutenfree; 

Il semble fonctionner, mais je ne peux pas comprendre comment dire si en effet il est que les lignes indexation où la valeur est vrai. Je veux m'assurer que ce n'est pas quelque chose de stupide comme indexer des lignes avec n'importe quelle valeur.

Dois-je ajouter un opérateur à la clause WHERE ou cette syntaxe est-elle parfaitement valide? J'espère que ce n'est pas l'une de ces questions RTFM super facile qui sera downviewée 30 fois.

MISE À JOUR:

Je suis allé de l'avant et ajouté 10.000 lignes à RecipeMetadata avec des valeurs aléatoires. J'ai ensuite fait un ANALYZE sur la table et un REINDEX juste pour être sûr. Quand je lance la requête:

select recipeid from RecipeMetadata where diet_glutenfree;

Je reçois:

'Seq Scan on recipemetadata (cost=0.00..214.26 rows=5010 width=16)' 
' Filter: diet_glutenfree' 

Ainsi, il semble faire une analyse séquentielle sur la table, même si seulement la moitié des lignes ont ce drapeau. L'index est ignoré.

Si je fais:

select recipeid from RecipeMetadata where not diet_glutenfree;

je reçois:

'Seq Scan on recipemetadata (cost=0.00..214.26 rows=5016 width=16)' 
' Filter: (NOT diet_glutenfree)' 

Donc, peu importe quoi, cet indice n'est pas utilisé.

+1

Veuillez ajouter un lien vers votre message de la liste de diffusion PostgreSQL à partir des archives pour que les utilisateurs puissent relier cette discussion à celle-ci. Ce serait bien si vous postez un suivi à votre message de la liste de diffusion avec un lien vers ceci, aussi. Si vous devez effectuer des post-croquis à plusieurs endroits, veuillez le dire pour éviter que les gens ne se répètent. –

+0

Pas de problème, je vais le faire à l'avenir (je ne poste généralement pas dans les deux endroits) .. –

+0

BTW, je pense que la réponse à votre question est "Oui" ... mais si vous êtes concerné , remplissez une table avec des données factices, 'ANALYZE' la table, puis utilisez' EXPLAIN ANALYZE' pour examiner les plans de certaines requêtes qui devraient toucher l'index partiel. –

Répondre

4

J'ai confirmé que l'index fonctionne comme prévu.

J'ai recréé les données aléatoires, cette fois seulement diet_glutenfree à random() > 0.9 donc il y a seulement 10% de chance d'un bit on. J'ai ensuite recréé les index et essayé à nouveau la requête.

SELECT RecipeId from RecipeMetadata where diet_glutenfree; 

Retours:

'Index Scan using idx_recipemetadata_glutenfree on recipemetadata (cost=0.00..135.15 rows=1030 width=16)' 
' Index Cond: (diet_glutenfree = true)' 

Et:

SELECT RecipeId from RecipeMetadata where NOT diet_glutenfree; 

Retours:

'Seq Scan on recipemetadata (cost=0.00..214.26 rows=8996 width=16)' 
' Filter: (NOT diet_glutenfree)' 

Il semble ma première tentative a été polluée puisque les estimations PG il est plus rapide pour scanner l'ensemble table plutôt que de frapper l'inde x s'il doit charger plus de la moitié des rangs de toute façon.

Cependant, je pense que j'obtiendrais ces résultats exacts sur un index complet de la colonne. Existe-t-il un moyen de vérifier le nombre de lignes indexées dans un index partiel?

MISE À JOUR

L'indice est d'environ 40k. J'ai créé un index complet de la même colonne et il est plus de 200k, donc il semble que c'est définitivement partiel.

+1

Yep, bang on. "Environ la moitié" des lignes ne fera pas que Pg favorise l'indice. Vous aurez besoin d'une sélectivité bien meilleure que 50% avant qu'un balayage d'index soit plus rapide qu'un seqscan. –

+0

Merci beaucoup! J'ai aussi créé un index complet pour comparer les tailles. Cela fonctionne vraiment comme prévu. –

+0

Note: vous semblez avoir seulement 10K enregistrements. L '«ensemble de travail» pour votre requête s'inscrira probablement dans le noyau. L'optimisation que vous effectuez est une optimisation en termes de cpu-usage. Une fois que le «working set» est plus grand que l'espace tampon disponible, votre requête deviendra liée aux E/S et l'index ne vous sera plus utile (à moins que vos lignes ne soient trop grandes pour une page disque). – wildplasser

1

Un index sur un champ à un bit n'a aucun sens. Pour comprendre les décisions prises par le planificateur, vous devez penser en termes de pages, pas en termes de lignes.

Pour les pages de 8 Ko et une taille (estinée) de 80, il y a 100 lignes sur chaque page. En supposant une distribution aléatoire, la chance qu'une page se compose uniquement de lignes avec une valeur true est négligeable, pow (0.5, 100), environ 1e-33, IICC. (et la même chose pour 'faux' bien sûr) Ainsi pour une requête sur gluten_free == true, chaque page doit être récupérée quand même, et filtrée ensuite. L'utilisation d'un index ne ferait qu'obtenir plus de pages (: l'index).

+1

"Un index sur un champ à un bit n'a aucun sens". Les booléens de Postgres nécessitent 8 bits de stockage: http://www.postgresql.org/docs/8.4/static/datatype-boolean.html "En supposant une distribution aléatoire" - ceci est potentiellement une grosse hypothèse. Beaucoup moins de 50% des aliments sont généralement sans gluten. Réponse perspicace, malgré tout. –

+0

"champ à un bit" était sur le contenu de l'information, pas sur la taille de stockage requise.Il pourrait y avoir une structure de stockage possible pour stocker/indexer/récupérer efficacement les champs de bits (think: judy-trees), ceux-ci pourraient avoir besoin de moins de pages, mais il sera difficile de les combiner avec les exigences ATOM pour un SGBDR. – wildplasser

Questions connexes