Étant donné une chaîne qui peut contenir plusieurs instances d'un délimiteur, je veux générer toutes les sous-chaînes commençant après ce caractère.
Par exemple, étant donné une chaîne comme 'a.b.c.d.e'
(ou un tableau {a,b,c,d,e}
, je suppose), je veux générer un tableau comme:
{a.b.c.d.e, b.c.d.e, c.d.e, d.e, e}
L'utilisation prévue est un déclencheur pour remplir une colonne pour une interrogation plus facile des parties de nom de domaine (c'est-à-dire trouver tout q.x.t.com
pour la requête t.com
) chaque fois qu'une autre colonne est écrite.
Cela semble être un moyen gênant de résoudre cela (et cela peut très bien l'être), mais maintenant je suis curieux de savoir comment une fonction comme celle-ci pourrait être écrite en (Postgres ') SQL.
Ce sont des noms de domaine de messagerie, il est donc difficile de dire quel est le nombre maximal d'éléments possibles, mais la grande majorité serait certainement <5.
la source
Réponses:
Je ne pense pas que vous ayez besoin d'une colonne distincte ici; c'est un problème XY. Vous essayez simplement de faire une recherche de suffixe. Il existe deux façons principales d'optimiser cela.
Transformez la requête de suffixe en requête de préfixe
Pour ce faire, vous inversez tout.
Créez d'abord un index au revers de votre colonne:
Ensuite, interrogez en utilisant la même chose:
Vous pouvez lancer un
UPPER
appel si vous souhaitez le rendre insensible à la casse:Index des trigrammes
L'autre option est les index trigrammes. Vous devriez certainement l'utiliser si vous avez besoin de requêtes infixes (
LIKE 'something%something'
ouLIKE '%something%'
tapez des requêtes).Activez d'abord l'extension d'index de trigramme:
(Cela devrait venir avec PostgreSQL prêt à l'emploi sans aucune installation supplémentaire.)
Créez ensuite un index trigramme sur votre colonne:
Sélectionnez ensuite:
Encore une fois, vous pouvez ajouter un
UPPER
pour le rendre insensible à la casse si vous le souhaitez:Votre question telle qu'elle est écrite
Les index de trigrammes fonctionnent en fait en utilisant une forme un peu plus générale de ce que vous demandez sous le capot. Il décompose la chaîne en morceaux (trigrammes) et construit un index basé sur ceux-ci. L'index peut ensuite être utilisé pour rechercher des correspondances beaucoup plus rapidement qu'une analyse séquentielle, mais pour des requêtes d'infixe ainsi que de suffixe et de préfixe. Essayez toujours d'éviter de réinventer ce que quelqu'un d'autre a développé lorsque vous le pouvez.
Crédits
Les deux solutions sont à peu près textuellement issues du choix d'une méthode de recherche de texte PostgreSQL . Je recommande fortement de lui donner une lecture pour une analyse détaillée des options de recherche de texte disponibles dans PotsgreSQL.
la source
Je pense que c'est mon préféré.
LIGNES
TABLEAUX
la source
LIGNES
OU
TABLEAUX
OU
la source
Question posée
Table de test:
CTE récursif dans une sous-requête LATÉRALE
Le
CROSS JOIN LATERAL
(, LATERAL
pour faire court) est sûr, car le résultat agrégé de la sous-requête renvoie toujours une ligne. Vous obtenez ...str = ''
dans la table de basestr IS NULL
dans la table de baseEnveloppé avec un constructeur de tableau bon marché dans la sous-requête, donc pas d'agrégation dans la requête externe.
Un exemple des fonctionnalités SQL, mais la surcharge rCTE peut empêcher des performances optimales.
Force brute pour un nombre trivial d'éléments
Pour votre cas avec un petit nombre d'éléments , une approche simple sans sous-requête peut être plus rapide:
En supposant un maximum de 5 éléments comme vous l'avez commenté. Vous pouvez facilement vous développer pour plus.
Si un domaine donné a moins d'éléments, les
substring()
expressions en excès renvoient NULL et sont supprimées pararray_remove()
.En fait, l'expression ci-dessus (
right(str, strpos(str, '.')
), imbriquée plusieurs fois peut être plus rapide (bien que difficile à lire) car les fonctions d'expression régulière sont plus chères.Un fork de la requête de @ Dudu
La requête intelligente de @ Dudu pourrait être améliorée avec
generate_subscripts()
:Également utilisé
LEFT JOIN LATERAL ... ON true
pour conserver les lignes possibles avec des valeurs NULL.Fonction PL / pgSQL
Logique similaire à celle du rCTE. Sensiblement plus simple et plus rapide que ce que vous avez:
Le
OUT
paramètre est renvoyé automatiquement à la fin de la fonction.Il n'est pas nécessaire de l'initialiser
result
, carNULL::text[] || text 'a' = '{a}'::text[]
.Cela ne fonctionne qu'avec
'a'
une saisie correcte.NULL::text[] || 'a'
(chaîne littérale) déclencherait une erreur car Postgres choisit l'array || array
opérateur.strpos()
renvoie0
si aucun point n'est trouvé,right()
renvoie donc une chaîne vide et la boucle se termine.C'est probablement la plus rapide de toutes les solutions ici.
Tous fonctionnent dans Postgres 9.3+
(à l'exception de la notation de tranche de tableau court
arr[3:]
. J'ai ajouté une limite supérieure dans le violon pour le faire fonctionner dans pg 9.3:.arr[3:999]
)SQL Fiddle.
Approche différente pour optimiser la recherche
Je suis avec @ jpmc26 (et vous-même): une approche complètement différente sera préférable. J'aime la combinaison de jpmc26
reverse()
et de atext_pattern_ops
.Un index de trigrammes serait supérieur pour les correspondances partielles ou floues. Mais comme vous n'êtes intéressé que par des mots entiers , la recherche en texte intégral est une autre option. Je m'attends à une taille d'index beaucoup plus petite et donc à de meilleures performances.
pg_trgm ainsi que les requêtes insensibles à la casse FTS , btw.
Les noms d'hôte comme
q.x.t.com
out.com
(mots avec des points en ligne) sont identifiés comme de type "hôte" et traités comme un seul mot. Mais il y a aussi la correspondance des préfixes dans FTS (qui semble parfois être négligée). Le manuel:En utilisant l'idée intelligente de @ jpmc26 avec
reverse()
, nous pouvons faire en sorte que cela fonctionne:Qui est pris en charge par un index:
Notez la
'simple'
configuration: nous ne voulons pas que le radical ou le dictionnaire des synonymes soit utilisé avec la'english'
configuration par défaut .Alternativement (avec une plus grande variété de requêtes possibles), nous pourrions utiliser la nouvelle capacité de recherche d'expression de la recherche de texte dans Postgres 9.6. Les notes de version:
Requete:
Remplacez dot (
'.'
) par space (' '
) pour empêcher l'analyseur de classer «t.com» comme nom d'hôte et utilisez plutôt chaque mot comme lexème distinct.Et un index correspondant pour l'accompagner:
la source
J'ai trouvé quelque chose de semi-réalisable, mais j'aimerais avoir des commentaires sur l'approche. J'ai écrit très peu de PL / pgSQL donc j'ai l'impression que tout ce que je fais est assez hacky et je suis surpris quand ça marche.
Néanmoins, c'est là que j'ai pu:
Cela fonctionne ainsi:
la source
J'utilise la fonction fenêtre:
Résultat:
la source
Une variante de la solution de @Dudu Markovitz, qui fonctionne également avec des versions de PostgreSQL qui ne reconnaissent pas (encore) [i:]:
la source