PostgreSQL peut-il utiliser des valeurs nulles dans ses index?

10

J'ai lu ce livre qui dit que

La base de données suppose que Indexed_Col IS NOT NULL couvre une plage trop grande pour être utile, de sorte que la base de données ne conduira pas à un index à partir de cette condition.

Je reconnais que le livre a plus de 10 ans, mais il s'est déjà avéré très utile - En utilisant les instructions glanées dans ses pages, j'ai accéléré une requête d'un facteur dix.

De plus, en exécutant EXPLAIN ANALYZEune SELECTrequête, j'ai constaté qu'aucun de mes index n'est utilisé, même si, par tous les droits, il devrait l'être.

Ainsi, ma question est:

En supposant qu'il existe une table qui a une colonne, dont la définition de colonne inclut "NOT NULL", et qu'il existe un index qui couvre cette colonne, cet index serait-il utilisé dans une requête de cette table où les colonnes font partie de la requête?

Comme:

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;
FuriousFolder
la source

Réponses:

9

PostgreSQL peut certainement utiliser un index pour IS NOT NULL. Je ne vois pas non plus d'hypothèses de planificateur de requêtes sur cette condition.

Si la fraction nulle de la colonne ( pg_statistic.stanullfrac) est suffisamment faible pour suggérer que l'index est sélectivement utile pour la requête, PostgreSQL utilise un index.

Je ne peux pas comprendre ce que vous essayez de dire avec:

Si c'est correct, est-ce que je comprends qu'un index sur une colonne définie comme "NOT NULL" ne doit pas être utilisé dans une requête qui utilise cette colonne?

Un index ne sera certainement pas utilisé pour une IS NOT NULLcondition sur une NOT NULLcolonne. Il correspondrait toujours à 100% des lignes, donc un seqscan sera presque toujours beaucoup plus rapide.

PostgreSQL n'utilisera pas d'index si l'index ne filtre pas une grande proportion de lignes pour une requête. La seule exception probable est lorsque vous demandez un ensemble de colonnes couvertes par un seul index, dans un ordre correspondant à celui de l'index. PostgreSQL pourrait alors effectuer une analyse d'index uniquement. Par exemple, s'il y a un index t(a, b, c)et que vous:

select a, b FROM t ORDER BY a, b, c;

PostgreSQL peut utiliser votre index, même si aucune ligne n'est filtrée, car il n'a qu'à lire l'index et peut ignorer la lecture du tas, éviter de faire un tri, etc.

Craig Ringer
la source
Tout cela est vrai à partir de PG 9.0
eradman
1
Et même sur une colonne nullable, une requête avec condition WHERE column IS NOT NULLpeut ne pas utiliser l'index car, comme le dit le livre: "couvre une plage trop grande pour être utile". Si 90% des valeurs ne sont pas nulles, un seqscan sera probablement plus rapide aussi.
ypercubeᵀᴹ
Exactement. C'est possible, mais seulement si une grande partie de la table est nulle. Souvent, dans ce cas, un indice partiel est de toute façon un meilleur choix.
Craig Ringer
Oui. J'essayais de dire que (si je comprends bien) la partie "couvre une plage trop large" se réfère à l'indice mais en ce qui concerne la condition spécifique et non l'indice en général.
ypercubeᵀᴹ
2
@FuriousFolder Heh, il y a trop de négations ici. PostgreSQL n'utilisera pas d'index sur une NOT NULLcolonne pour une IS NOT NULLrequête, à moins que cet index ne soit également utile pour d'autres parties de la WHEREclause, les filtres de jointure, etc., ou qu'il soit utilisable pour une analyse ordonnée uniquement par index. En d'autres termes, il ignorera complètement le redondant IS NOT NULLsur la NOT NULLcolonne et fera des choix d'utilisation d'index en fonction d'autres détails. (Voir éditer, re analyses uniquement indexées).
Craig Ringer
2

En plus de la réponse complète de Craig, je voulais ajouter que la couverture du livre auquel vous faites référence dit:

Couvre Oracle, DB2 et SQL Server

Je ne lui ferais donc pas confiance pour être une excellente source de conseils sur PostgreSQL en particulier. Chaque SGBDR peut être étonnamment différent!

Je suis un peu confus au sujet de votre question d'origine, mais voici un exemple montrant que la section du livre n'est pas correcte à 100%. Pour éviter toute confusion, voici l'intégralité du paragraphe pertinent, vous pouvez le voir dans Google Recherche de Livres .

La base de données suppose que Indexed_Col IS NOT NULL couvre une plage trop grande pour être utile, de sorte que la base de données ne conduira pas à un index à partir de cette condition. Dans de rares cas, avoir une valeur non nulle est si rare qu'un balayage de plage d'index sur toutes les valeurs non nulles possibles est bénéfique. Dans de tels cas, si vous pouvez déterminer une limite inférieure ou supérieure sûre pour la plage de toutes les valeurs possibles, vous pouvez activer un balayage de plage avec une condition telle que Positive_ID_Column> -1 ou Date_Column> TO_DATE ('0001/01/01' , «AAAA / MM / JJ»).

Postgres peut réellement (dans le cas artificiel suivant) utiliser un index pour satisfaire les IS NOT NULLrequêtes sans ajouter de contraintes de balayage de plage comme suggéré Positive_ID_Column > -1. Voir les commentaires sur les questions de Craig pour savoir pourquoi Postgres choisit cet index dans ce cas particulier, et la note sur l'utilisation des index partiels.

CREATE TABLE bar (a int);
INSERT INTO bar (a) SELECT NULL FROM generate_series(1,1000000);
INSERT INTO bar (a) VALUES (1);
CREATE INDEX bar_idx ON bar (a);

EXPLAIN ANALYZE SELECT * FROM bar WHERE a IS NOT NULL;
                                                QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Only Scan using bar_idx on bar  (cost=0.42..8.44 rows=1 width=4) (actual time=0.094..0.095 rows=1 loops=1)
   Index Cond: (a IS NOT NULL)
   Heap Fetches: 1
 Total runtime: 0.126 ms
(4 rows)

Il s'agit en fait de Postgres 9.3, mais je pense que les résultats seraient à peu près similaires sur 9.1, bien qu'il n'utilise pas de "Index Only Scan".

Edit: Je vois que vous avez clarifié votre question d'origine, et vous vous demandez apparemment pourquoi Postgres n'utilise pas d'index dans un exemple simple comme:

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;

Probablement parce que vous n'avez aucune ligne dans le tableau. Ajoutez donc des données de test et ANALYZE my_table;.

Josh Kupershmidt
la source
Dans la description dudit livre (c'est moi qui souligne): "L'auteur Dan Tow décrit une méthode permettant de gagner du temps, qu'il a développée pour trouver le plan d'exécution optimal - rapidement et systématiquement - quelle que soit la complexité du SQL ou de la plate-forme de base de données utilisée ". vous avez peut-être ignoré le n ° 1 de la question, à savoir que la colonne est définie comme NOT NULL, et non que la requête utilise IS NOT NULLcomme condition d'index. C'est dans les commentaires que vous avez référencés, mais je mettrai à jour la question pour l'inclure.
FuriousFolder
De plus, le livre lui-même est indépendant de la langue: les seules parties spécifiques à DMBS concernent l'affichage des plans de requête, ce que Postgres rend assez simple :)
FuriousFolder
1
@FuriousFolder la colonne est définie comme NOT NULL mais cette partie (dans votre question, du livre): "que Indexed_Col IS NOT NULL couvre ..." fait référence à la condition where et non à la définition de la colonne. Bien qu'il soit difficile d'en être sûr, c'est hors contexte. Vous devriez peut-être inclure tout le paragraphe (précédent) du livre.
ypercubeᵀᴹ
-1

Vous n'avez pas publié votre requête ou exemple de données. Mais la raison la plus courante pour laquelle les index ne sont pas utilisés est le volume.

Les index sont comme un annuaire téléphonique qui traduit une colonne en un emplacement de ligne. Si vous ne recherchez que quelques lignes, il est logique de rechercher chaque ligne du répertoire, puis de rechercher la ligne dans le tableau principal.

Mais pour plus de quelques lignes, il est moins cher d'ignorer le répertoire téléphonique et d'itérer sur toutes les lignes de la table principale. D'après mon expérience, le point de basculement est d'environ 100 lignes.

Andomar
la source
"Les index sont comme un répertoire qui traduit une colonne en un emplacement de ligne. Si vous ne recherchez que quelques lignes, il est logique de rechercher chaque ligne du répertoire, puis de rechercher la ligne dans le tableau principal." En fait, les index sont comme des répertoires plus petits qui sont mis à jour chaque fois que le répertoire qu'ils indexent est mis à jour. Vous savez que chaque fois que vous ouvrirez un répertoire plus petit, vous trouverez toutes les informations décrites par sa condition d'indexation. Par exemple , toutes les personnes nommées « Frank » sur une table d'index: CREATE INDEX ix_frank ON people(name) WHERE name ='frank'.
FuriousFolder
Cela permet une analyse indexée uniquement beaucoup plus rapide, car vous pouvez lire l'intégralité du "répertoire téléphonique plus petit" en mémoire, ce qui n'est pas possible avec une table de plusieurs millions de lignes.
FuriousFolder
@FuriousFolder: vous décrivez une analyse indexée uniquement. Mais l'OP dit que ses index ne sont pas utilisés, ce qui ne se produirait pas si un scan d'index seul satisfaisait la requête.
Andomar
Andomar ... je suis l'OP, haha. Mon objectif est exactement cela; pour que cette requête utilise une analyse d'index uniquement. Je l'ai depuis réalisé, car Craig a expliqué que postgres est capable d'utiliser un index sur une colonne où la définition de la colonne comprend NOT NULL
FuriousFolder