Je devais écrire une requête simple où je cherchais le nom des personnes commençant par un B ou un D:
SELECT s.name
FROM spelers s
WHERE s.name LIKE 'B%' OR s.name LIKE 'D%'
ORDER BY 1
Je me demandais s'il y avait un moyen de réécrire cela pour devenir plus performant. Donc je peux éviter or
et / ou like
?
postgresql
performance
index
regular-expression
pattern-matching
Lucas Kauffman
la source
la source
s.name
indexé?name
pourrait être utile ici si vous vous souciez de la performance.Réponses:
Votre requête est à peu près l'optimum. La syntaxe ne sera pas beaucoup plus courte, la requête ne sera pas beaucoup plus rapide:
Si vous voulez vraiment raccourcir la syntaxe , utilisez une expression régulière avec des branches :
Ou légèrement plus vite, avec une classe de personnage :
Un test rapide sans index donne des résultats plus rapides que
SIMILAR TO
dans les deux cas pour moi.Avec un index B-Tree approprié en place,
LIKE
remporte cette course par ordre de grandeur.Lisez les bases sur la correspondance des modèles dans le manuel .
Indice pour une performance supérieure
Si les performances vous préoccupent, créez un index comme celui-ci pour les tables plus volumineuses:
Rend ce genre de requête plus rapidement par ordre de grandeur. Des considérations spéciales s'appliquent à l'ordre de tri spécifique à l'environnement local. En savoir plus sur les classes d'opérateurs dans le manuel . Si vous utilisez les paramètres régionaux "C" standard (la plupart des gens ne le font pas), un index simple (avec la classe d'opérateur par défaut) fera l'affaire.
Un tel index n'est utile que pour les modèles ancrés à gauche (correspondant dès le début de la chaîne).
SIMILAR TO
ou des expressions régulières avec des expressions de base ancrées à gauche peuvent également utiliser cet index. Mais pas avec des branches(B|D)
ou des classes de caractères[BD]
(du moins dans mes tests sur PostgreSQL 9.0).Les correspondances de trigrammes ou la recherche de texte utilisent des index spéciaux GIN ou GiST.
Vue d'ensemble des opérateurs de filtrage
LIKE
(~~
) est simple et rapide, mais limité dans ses capacités.ILIKE
(~~*
) la variante insensible à la casse.pg_trgm étend le support d'index pour les deux.
~
(correspondance d'expression régulière) est puissant mais plus complexe et peut être lent pour autre chose que des expressions de base.SIMILAR TO
est juste inutile . Demi-sang particulierLIKE
et expressions régulières. Je ne l'utilise jamais. Voir ci-dessous.% est l'opérateur de "similarité" fourni par le module supplémentaire
pg_trgm
. Voir ci-dessous.@@
est l'opérateur de recherche de texte. Voir ci-dessous.pg_trgm - correspondance de trigramme
À partir de PostgreSQL 9.1, vous pouvez faciliter l’extension
pg_trgm
pour fournir un support d’index pour tout motifLIKE
/ILIKE
pattern (et de simples modèles d’expression rationnelle avec~
) à l’aide d’un index GIN ou GiST.Détails, exemple et liens:
pg_trgm
fournit également ces opérateurs :%
- l'opérateur "similitude"<%
(commutateur%>
:) - l'opérateur "word_similarity" dans Postgres 9.6 ou version ultérieure<<%
(commutateur%>>
:) - l'opérateur "strict_word_similarity" dans Postgres 11 ou version ultérieureRecherche de texte
Est un type spécial de correspondance de modèle avec des types d’infrastructure et d’index séparés. Il utilise des dictionnaires et est un outil génial pour trouver des mots dans les documents, en particulier pour les langues naturelles.
La correspondance de préfixe est également prise en charge:
Ainsi que la recherche de phrase depuis Postgres 9.6:
Considérez l’ introduction dans le manuel et l’ aperçu des opérateurs et des fonctions .
Outils supplémentaires pour la correspondance de chaîne fuzzy
Le module supplémentaire fuzzystrmatch offre quelques options supplémentaires, mais les performances sont généralement inférieures à toutes les solutions ci-dessus.
En particulier, diverses implémentations de la
levenshtein()
fonction peuvent être déterminantes.Pourquoi les expressions régulières (
~
) sont-elles toujours plus rapides queSIMILAR TO
?La réponse est simple
SIMILAR TO
les expressions sont réécrites en expressions régulières en interne. Ainsi, pour chaqueSIMILAR TO
expression, il existe au moins une expression régulière plus rapide (qui évite la surcharge de la réécriture de l'expression). Il n'y a aucun gain de performance en utilisantSIMILAR TO
jamais .Et de toute façon, les expressions simples pouvant être réalisées avec
LIKE
(~~
) sont plus rapidesLIKE
.SIMILAR TO
n’est supporté que par PostgreSQL car il s’est retrouvé dans les premières versions du standard SQL. Ils ne s'en sont toujours pas débarrassés. Mais il est prévu de l'enlever et d'inclure plutôt des correspondances d'expressions rationnelles - ou du moins l'ai-je entendu dire.EXPLAIN ANALYZE
le révèle. Essayez juste avec n'importe quelle table vous-même!Révèle:
SIMILAR TO
a été réécrit avec une expression régulière (~
).Performance ultime pour ce cas particulier
Mais
EXPLAIN ANALYZE
révèle plus. Essayez, avec l'index susmentionné en place:Révèle:
En interne, avec un indice qui est locale-conscient de ne pas (
text_pattern_ops
ou à l' aide localeC
) simples expressions ancrées à gauche sont réécrites avec ces opérateurs de modèle de texte:~>=~
,~<=~
,~>~
,~<~
. C'est le cas pour~
,~~
ouSIMILAR TO
semblable.Il en va de même pour les index sur les
varchar
types avecvarchar_pattern_ops
ouchar
avecbpchar_pattern_ops
.Donc, appliqué à la question initiale, c’est le moyen le plus rapide possible :
Bien sûr, s'il vous arrivait de rechercher des initiales adjacentes , vous pouvez simplifier:
Le gain par rapport à l'utilisation simple de
~
ou~~
est minime. Si les performances ne sont pas votre exigence primordiale, vous devez simplement vous en tenir aux opérateurs standard pour arriver à ce que vous avez déjà dans la question.la source
similar
un balayage?EXPLAIN ANALYZE
deux analyses d'index bitmap. Plusieurs analyses d'index bitmap peuvent être combinées assez rapidement.OR
par parUNION ALL
ouname LIKE 'B%'
parname >= 'B' AND name <'C'
Postgres?UNION
ne le fera pas mais, oui, la combinaison des plages dans uneWHERE
clause accélérera la requête. J'ai ajouté plus à ma réponse. Bien sûr, vous devez tenir compte de vos paramètres régionaux. La recherche tenant compte des paramètres régionaux est toujours plus lente.Que diriez-vous d'ajouter une colonne à la table. En fonction de vos besoins réels:
PostgreSQL ne prend pas en charge les colonnes calculées dans les tables de base à la manière de SQL Server, mais la nouvelle colonne peut être gérée via un déclencheur. De toute évidence, cette nouvelle colonne serait indexée.
Alternativement, un index sur une expression vous donnerait la même chose, moins cher. Par exemple:
Les requêtes qui correspondent à l'expression dans leurs conditions peuvent utiliser cet index.
De cette façon, la performance est prise au moment de la création ou de la modification des données; elle ne peut donc être appropriée que dans un environnement peu actif (c.-à-d. Beaucoup moins d'écritures que de lectures).
la source
Tu pourrais essayer
Je ne sais pas si ce qui précède ou votre expression originale sont vraisemblables dans Postgres.
Si vous créez l'index suggéré, vous voudriez également savoir comment il se compare aux autres options.
la source
Par le passé, face à un problème de performances similaire, j’ai incrémenté le caractère ASCII de la dernière lettre et effectué un BETWEEN. Vous obtenez alors les meilleures performances, pour un sous-ensemble de la fonctionnalité LIKE. Bien sûr, cela ne fonctionne que dans certaines situations, mais pour les très grands ensembles de données dans lesquels vous recherchez un nom, par exemple, les performances deviennent catastrophiques à acceptables.
la source
Très vieille question, mais j'ai trouvé une autre solution rapide à ce problème:
Depuis la fonction ascii () ne regarde que le premier caractère de la chaîne.
la source
(name)
?Pour vérifier les initiales, j’utilise souvent le casting avec
"char"
(avec les guillemets). Ce n'est pas portable, mais très rapide. En interne, il détache simplement le texte et renvoie le premier caractère. Les opérations de comparaison "char" sont très rapides car le type est une longueur fixe de 1 octet:Notez que le casting sur
"char"
est plus rapide que laascii()
résolution de @ Sole021, mais il n'est pas compatible UTF8 (ni aucun autre codage d'ailleurs), renvoyant simplement le premier octet. caractères ASCII à 2 bits.la source
Deux méthodes ne sont pas encore mentionnées pour traiter de tels cas:
index partiel (ou partitionné - s'il est créé manuellement pour l'ensemble de la plage) - utile lorsque seulement un sous-ensemble de données est requis (par exemple, lors de certaines tâches de maintenance ou temporaire pour certains rapports):
partitionner la table elle-même (en utilisant le premier caractère comme clé de partitionnement) - cette technique est particulièrement intéressante dans PostgreSQL 10+ (partitionnement moins pénible) et 11+ (élagage de partition lors de l'exécution de la requête).
De plus, si les données d'une table sont triées, l'utilisation de l' index BRIN (sur le premier caractère) peut être avantageuse .
la source
Probablement plus rapide de faire une comparaison de caractère unique:
la source
column LIKE 'B%'
sera plus efficace que d'utiliser la fonction de sous-chaîne sur la colonne.