Dada uma sequência que pode conter várias instâncias de um delimitador, desejo gerar todas as substrings iniciadas após esse caractere.
Por exemplo, dada uma sequência como 'a.b.c.d.e'
(ou matriz {a,b,c,d,e}
, suponho), quero gerar uma matriz como:
{a.b.c.d.e, b.c.d.e, c.d.e, d.e, e}
O uso pretendido é como um gatilho para preencher uma coluna para facilitar a consulta de partes de nomes de domínio (ou seja, encontrar tudo q.x.t.com
para consulta t.com
) sempre que outra coluna for gravada.
Parece uma maneira incômoda de resolver isso (e pode muito bem ser), mas agora estou curioso para saber como uma função como essa pode ser escrita no SQL (do Postgres).
Como são nomes de domínio de email, é difícil dizer qual é o número máximo possível de elementos, mas certamente a grande maioria seria <5.
fonte
Respostas:
Eu não acho que você precise de uma coluna separada aqui; este é um problema XY. Você está apenas tentando fazer uma pesquisa por sufixo. Existem duas maneiras principais de otimizar isso.
Transforme a consulta de sufixo em uma consulta de prefixo
Você basicamente faz isso revertendo tudo.
Primeiro, crie um índice no verso da sua coluna:
Em seguida, consulte usando o mesmo:
Você pode fazer uma
UPPER
chamada se quiser torná-la sem distinção entre maiúsculas e minúsculas:Índices de Trigrama
A outra opção é índices de trigrama. Você definitivamente deve usar isso se precisar de consultas infix (
LIKE 'something%something'
ouLIKE '%something%'
consultas de tipo).Primeiro, ative a extensão do índice trigrama:
(Isso deve vir com o PostgreSQL pronto para uso, sem nenhuma instalação extra.)
Em seguida, crie um índice trigrama na sua coluna:
Depois, selecione:
Novamente, você pode inserir um
UPPER
para torná-lo sem distinção entre maiúsculas e minúsculas, se desejar:Sua pergunta como está escrita
Os índices de trigrama realmente funcionam usando uma forma um pouco mais geral do que você está pedindo sob o capô. Ele divide a cadeia em pedaços (trigramas) e cria um índice com base neles. O índice pode então ser usado para procurar correspondências muito mais rapidamente do que uma varredura seqüencial, mas para consultas de infixo, bem como sufixos e prefixos. Sempre tente evitar reinventar o que outra pessoa desenvolveu quando puder.
Créditos
As duas soluções são praticamente verbais na escolha de um método de pesquisa de texto do PostgreSQL . Eu recomendo ler para uma análise detalhada das opções de pesquisa de texto disponíveis no PotsgreSQL.
fonte
Eu acho que esse é o meu favorito.
ROWS
ARRAYS
fonte
ROWS
OU
ARRAYS
OU
fonte
Pergunta feita
Tabela de teste:
CTE recursiva em uma subconsulta LATERAL
O
CROSS JOIN LATERAL
(, LATERAL
abreviado) é seguro, porque o resultado agregado da subconsulta sempre retorna uma linha. Você consegue ...str = ''
na tabela basestr IS NULL
na tabela baseEmbrulhado com um construtor de matriz barato na subconsulta, portanto, sem agregação na consulta externa.
Uma demonstração dos recursos SQL, mas a sobrecarga do rCTE pode impedir o desempenho superior.
Força bruta para número trivial de elementos
Para o seu caso com um número trivialmente pequeno de elementos , uma abordagem simples sem subconsulta pode ser mais rápida:
Supondo um máximo de 5 elementos como você comentou. Você pode facilmente expandir para mais.
Se um determinado domínio tiver menos elementos,
substring()
expressões em excesso retornarão NULL e serão removidas porarray_remove()
.Na verdade, a expressão acima (
right(str, strpos(str, '.')
), aninhada várias vezes, pode ser mais rápida (embora de difícil leitura), pois as funções de expressão regular são mais caras.Uma bifurcação da consulta de @ Dudu
A consulta inteligente do @ Dudu pode ser melhorada com
generate_subscripts()
:Também usando
LEFT JOIN LATERAL ... ON true
para preservar possíveis linhas com valores NULL.Função PL / pgSQL
Lógica semelhante à do rCTE. Substancialmente mais simples e mais rápido do que você tem:
O
OUT
parâmetro é retornado no final da função automaticamente.Não há necessidade de inicializar
result
, porqueNULL::text[] || text 'a' = '{a}'::text[]
.Isso funciona apenas com a
'a'
digitação correta.NULL::text[] || 'a'
(string literal) geraria um erro porque o Postgres escolhe oarray || array
operador.strpos()
retorna0
se nenhum ponto for encontrado, entãoright()
retorna uma string vazia e o loop termina.Esta é provavelmente a mais rápida de todas as soluções aqui.
Todos eles funcionam no Postgres 9.3+ (exceto pela notação de fatia de matriz curta . Adicionei um limite superior no violino para fazê-lo funcionar na página 9.3:. )
arr[3:]
arr[3:999]
SQL Fiddle.
Abordagem diferente para otimizar a pesquisa
Estou com @ jpmc26 (e com você): uma abordagem completamente diferente será preferível. Eu gosto da combinação de jpmc26 de
reverse()
e atext_pattern_ops
.Um índice de trigrama seria superior para correspondências parciais ou difusas. Mas como você só está interessado em palavras inteiras , a Pesquisa de texto completo é outra opção. Espero um tamanho de índice substancialmente menor e, portanto, melhor desempenho.
O pg_trgm e o FTS oferecem suporte a consultas sem distinção entre maiúsculas e minúsculas , btw.
Nomes de host como
q.x.t.com
out.com
(palavras com pontos embutidos) são identificados como tipo "host" e tratados como uma palavra. Mas também há correspondência de prefixo no STF (que às vezes parece ser esquecido). O manual:Usando a idéia inteligente do @ jpmc26
reverse()
, podemos fazer este trabalho:O que é suportado por um índice:
Observe a
'simple'
configuração: não queremos que o stemming ou o thesaurus sejam usados com a'english'
configuração padrão .Como alternativa (com uma variedade maior de consultas possíveis), poderíamos usar o novo recurso de pesquisa de frase da pesquisa de texto no Postgres 9.6. As notas de versão:
Inquerir:
Substitua dot (
'.'
) por space (' '
) para impedir que o analisador classifique 't.com' como nome do host e, em vez disso, use cada palavra como léxico máximo.E um índice correspondente para acompanhar:
fonte
Eu criei algo semi-viável, mas eu adoraria comentários sobre a abordagem. Eu escrevi muito pouco PL / pgSQL, então sinto que tudo o que faço é bastante hacky e fico surpreso quando funciona.
No entanto, é aqui que eu cheguei:
Isso funciona assim:
fonte
Eu uso a função de janela:
Resultado:
fonte
Uma variante da solução de @Dudu Markovitz, que também funciona com versões do PostgreSQL que ainda não reconhecem [i:]:
fonte