Dans une base de données Postgres 9.1, j'ai une table table1
avec environ 1,5 million de lignes et une colonne label
(noms simplifiés pour cette question).
Il existe un trigram-index fonctionnel lower(unaccent(label))
( unaccent()
a été rendu immuable pour permettre son utilisation dans l'index).
La requête suivante est assez rapide:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword%')));
count
-------
1
(1 row)
Time: 394,295 ms
Mais la requête suivante est plus lente:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword and some more%')));
count
-------
1
(1 row)
Time: 1405,749 ms
Et ajouter plus de mots est encore plus lent, même si la recherche est plus stricte.
J'ai essayé une astuce simple pour exécuter une sous-requête pour le premier mot, puis une requête avec la chaîne de recherche complète, mais (malheureusement) le planificateur de requêtes a vu à travers mes machinations:
EXPLAIN ANALYZE
SELECT * FROM (
SELECT id, title, label from table1
WHERE lower(unaccent(label)) like lower(unaccent('%someword%'))
) t1
WHERE lower(unaccent(label)) like lower(unaccent('%someword and some more%'));
Bitmap Heap Scan sur table1 (coût = 16216.01..16220.04 lignes = 1 largeur = 212) (temps réel = 1824.017..1824.019 lignes = 1 boucles = 1) Vérifiez à nouveau Cond: ((lower (unaccent ((label) :: text)) ~~ '% someword%' :: text) AND (lower (unaccent ((label) :: text)) ~~ '% someword et quelques autres %'::texte)) -> Bitmap Index Scan sur table1_label_hun_gin_trgm (coût = 0,00..16216,01 lignes = 1 largeur = 0) (temps réel = 1823,900..1823,900 lignes = 1 boucles = 1) Index Cond: ((lower (unaccent ((label) :: text)) ~~ '% someword%' :: text) AND (lower (unaccent ((label) :: text)) ~~ '% someword et quelques autres %'::texte)) Durée d'exécution totale: 1824,064 ms
Mon problème ultime est que la chaîne de recherche provient d'une interface Web qui peut envoyer des chaînes assez longues et donc être assez lente et peut également constituer un vecteur DOS.
Mes questions sont donc:
- Comment accélérer la requête?
- Existe-t-il un moyen de le diviser en sous-requêtes afin qu'il soit plus rapide?
- Peut-être qu'une version ultérieure de Postgres est meilleure? (J'ai essayé 9.4 et ça ne semble pas plus rapide: toujours le même effet. Peut-être une version plus récente?)
- Peut-être qu'une stratégie d'indexation différente est nécessaire?
unaccent
immuable. J'ai ajouté ceci à la question.
unaccent
module. L'une des raisons pour lesquelles je suggère un wrapper de fonction à la place.
unaccent()
est également fourni par un module supplémentaire et Postgres ne prend pas en charge les index sur la fonction par défaut car ce n'est pas le casIMMUTABLE
. Vous devez avoir modifié quelque chose et vous devez mentionner exactement ce que vous avez fait dans votre question. Mon conseil permanent: stackoverflow.com/a/11007216/939860 . De plus, les index trigrammes prennent en charge la correspondance insensible à la casse prête à l'emploi. Vous pouvez simplifier pour:WHERE f_unaccent(label) ILIKE f_unaccent('%someword%')
- avec un index correspondant. Détails: stackoverflow.com/a/28636000/939860 .