2010-08-12 5 views
22

je une requête de la forme:indexation valeurs NULL dans PostgreSQL

select m.id from mytable m 
left outer join othertable o on o.m_id = m.id 
    and o.col1 is not null and o.col2 is not null and o.col3 is not null 
where o.id is null 

La requête renvoie quelques centaines de dossiers, bien que les tables ont des millions de lignes, et il faut toujours courir (environ une heure) .

Quand je vérifie mes statistiques d'index à l'aide:

select * from pg_stat_all_indexes 
where schemaname <> 'pg_catalog' and (indexrelname like 'othertable_%' or indexrelname like 'mytable_%') 

Je vois que seul l'index pour othertable.m_id est utilisé, et que les indices pour col1..3 ne sont pas utilisés du tout. Pourquoi est-ce?

J'ai lu dans un fewplaces que PG n'a traditionnellement pas pu indexer les valeurs NULL. Cependant, j'ai lu que cela a soi-disant changé depuis PG 8.3? J'utilise actuellement PostgreSQL 8.4 sur Ubuntu 10.04. Ai-je besoin de créer un index "partiel" ou "fonctionnel" spécifiquement pour accélérer les requêtes IS NOT NULL, ou est-ce qu'il indexe déjà les valeurs NULL et que je ne comprends pas le problème?

Répondre

30

Vous pouvez essayer un index partiel:

CREATE INDEX idx_partial ON othertable (m_id) 
WHERE (col1 is not null and col2 is not null and col3 is not null); 

De la documentation: http://www.postgresql.org/docs/current/interactive/indexes-partial.html

+0

Cela a fonctionné parfaitement pour moi. Une requête de test qui a duré plusieurs minutes s'exécute maintenant en quelques secondes avec cet index. – Cerin

1

Un seul index sur m_id, col1, col2 et o.col3 serait ma première pensée pour cette requête.

Et d'utiliser EXPLAIN sur cette requête pour voir comment elle est exécutée et ce qui prend tellement de temps. Vous pourriez nous montrer les résultats pour vous aider.

1

Un partial index semble la voie ici:

Si vous avez une table qui contient à la fois commandes facturées et non facturées, où les commandes non facturées prennent une petite fraction du tableau total et pourtant ce sont les lignes les plus accédées, vous pouvez améliorer les performances en créant un index sur les lignes non facturées.

Peut-être ces colonnes nullable (col1, col2, col3) agissent dans votre scénario comme une sorte de drapeau pour distinguer une sous-classe d'enregistrements dans votre table? (par exemple, une sorte de "suppression logique")? Dans ce cas, en plus de la solution d'index partiel, vous préférerez peut-être repenser votre design et le placer dans différentes tables physiques (en utilisant éventuellement l'héritage), un pour les "live records" et les autres pour les "historiques". (seulement si nécessaire) à travers une vue.

5

Les index partiels ne vont pas vous aider ici car ils ne trouveront que les enregistrements que vous ne voulez pas. Vous voulez créer un index qui contient les enregistrements que vous voulez.

CREATE INDEX findDaNulls ON othertable ((COALESCE(col1,col2,col3,'Empty'))) 
WHERE col1 IS NULL AND col2 IS NULL AND col3 IS NULL; 

SELECT * 
FROM mytable m 
JOIN othertable o ON m.id = o.m_id 
WHERE COALESCE(col1,col2,col3,'Empty') = 'Empty'; 

BTW recherche de gauche est généralement nulle rejoint pas aussi rapide que l'utilisation EXISTS ou NOT EXISTS dans Postgres.

+1

Ceci est une idée intéressante, mais ne correspond pas exactement à ma logique. Votre solution trouve tous les enregistrements othertables où les colonnes * all * sont NULL. Dans mon exemple, je m'intéresse à tous les enregistrements où * au moins * une colonne est NULL. – Cerin

0

Avez-vous essayé de créer un index combiné sur othertable (m_id, col1, col2, col3)?

Vous devez également vérifier le plan d'exécution (à l'aide d'EXPLAIN) plutôt que de vérifier l'utilisation de l'index dans les tables système.

PostgreSQL 9.0 (actuellement en version bêta) pourra utiliser et indexer pour une condition IS NULL. Cette fonctionnalité a été reportée

+0

Avez-vous une référence? – vincent