Eu tive que escrever uma consulta simples em que vou procurar o nome das pessoas que começam com um B ou um D:
SELECT s.name
FROM spelers s
WHERE s.name LIKE 'B%' OR s.name LIKE 'D%'
ORDER BY 1
Fiquei me perguntando se existe uma maneira de reescrever isso para se tornar mais eficiente. Para que eu possa evitar or
e / ou like
?
postgresql
performance
index
regular-expression
pattern-matching
Lucas Kauffman
fonte
fonte
s.name
indexado?name
possa ser útil aqui, se você se preocupa com o desempenho.Respostas:
Sua consulta é praticamente a ideal. A sintaxe não ficará muito mais curta, a consulta não ficará muito mais rápida:
Se você realmente deseja reduzir a sintaxe , use uma expressão regular com branches :
Ou um pouco mais rápido, com uma classe de personagem :
Um teste rápido sem índice gera resultados mais rápidos do que
SIMILAR TO
em ambos os casos para mim.Com um índice B-Tree apropriado,
LIKE
vence esta corrida por ordens de magnitude.Leia o básico sobre correspondência de padrões no manual .
Índice para desempenho superior
Se você está preocupado com o desempenho, crie um índice como este para tabelas maiores:
Torna esse tipo de consulta mais rápido em ordens de magnitude. Considerações especiais se aplicam à ordem de classificação específica do código do idioma. Leia mais sobre classes de operadores no manual . Se você estiver usando o código de idioma "C" padrão (a maioria das pessoas não), um índice simples (com classe de operador padrão) será suficiente.
Esse índice é bom apenas para padrões ancorados à esquerda (correspondendo desde o início da string).
SIMILAR TO
ou expressões regulares com expressões ancoradas à esquerda básicas também podem usar esse índice. Mas não com ramos(B|D)
ou classes de caracteres[BD]
(pelo menos nos meus testes no PostgreSQL 9.0).As correspondências de trigrama ou a pesquisa de texto usam índices especiais GIN ou GiST.
Visão geral dos operadores de correspondência de padrões
LIKE
(~~
) é simples e rápido, mas limitado em suas capacidades.ILIKE
(~~*
) a variante que não diferencia maiúsculas de minúsculas.pg_trgm estende o suporte ao índice para ambos.
~
(correspondência de expressão regular) é poderoso, mas mais complexo e pode ser lento para algo além de expressões básicas.SIMILAR TO
é apenas inútil . Um mestiço peculiarLIKE
e expressões regulares. Eu nunca uso isso. Ver abaixo.% é o operador "similaridade", fornecido pelo módulo adicional
pg_trgm
. Ver abaixo.@@
é o operador de pesquisa de texto. Ver abaixo.pg_trgm - correspondência de trigrama
A partir do PostgreSQL 9.1, você pode facilitar a extensão
pg_trgm
para fornecer suporte ao índice para qualquer padrãoLIKE
/ILIKE
(e padrões simples de regexp~
) usando um índice GIN ou GiST.Detalhes, exemplo e links:
pg_trgm
também fornece esses operadores :%
- o operador "similaridade"<%
(comutador%>
:) - o operador "word_similarity" no Postgres 9.6 ou posterior<<%
(comutador%>>
:) - o operador "strict_word_similarity" no Postgres 11 ou posteriorPesquisa de texto
É um tipo especial de correspondência de padrões com tipos de infraestrutura e índice separados. Ele usa dicionários e stemming e é uma ótima ferramenta para encontrar palavras em documentos, especialmente para idiomas naturais.
A correspondência de prefixo também é suportada:
Assim como a pesquisa de frases desde o Postgres 9.6:
Considere a introdução no manual e a visão geral dos operadores e funções .
Ferramentas adicionais para correspondência de seqüência difusa
O módulo adicional fuzzystrmatch oferece mais algumas opções, mas o desempenho geralmente é inferior a todos os itens acima.
Em particular, várias implementações da
levenshtein()
função podem ser instrumentais.Por que as expressões regulares (
~
) são sempre mais rápidas queSIMILAR TO
?A resposta é simples.
SIMILAR TO
expressões são reescritas em expressões regulares internamente. Portanto, para cadaSIMILAR TO
expressão, há pelo menos uma expressão regular mais rápida (que economiza a sobrecarga de reescrever a expressão). Não há ganho de desempenho ao usarSIMILAR TO
sempre .E expressões simples que podem ser feitas com
LIKE
(~~
) são mais rápidas deLIKE
qualquer maneira.SIMILAR TO
só é suportado no PostgreSQL porque acabou nos primeiros rascunhos do padrão SQL. Eles ainda não se livraram disso. Mas há planos para removê-lo e incluir correspondências regexp - ou pelo menos ouvi dizer.EXPLAIN ANALYZE
revela isso. Apenas tente com qualquer mesa!Revela:
SIMILAR TO
foi reescrito com uma expressão regular (~
).Melhor desempenho para este caso em particular
Mas
EXPLAIN ANALYZE
revela mais. Tente, com o índice mencionado anteriormente:Revela:
Internamente, com um índice que não está locale-aware (
text_pattern_ops
ou usando localC
) expressões simples ancorado-esquerda são reescritas com estes operadores padrão de texto:~>=~
,~<=~
,~>~
,~<~
. Este é o caso~
,~~
ouSIMILAR TO
similar.O mesmo vale para índices em
varchar
tipos comvarchar_pattern_ops
ouchar
combpchar_pattern_ops
.Portanto, aplicada à pergunta original, esta é a maneira mais rápida possível :
Obviamente, se você procurar iniciais adjacentes , poderá simplificar ainda mais:
O ganho sobre o uso simples de
~
ou~~
é pequeno. Se o desempenho não for seu requisito primordial, você deve apenas manter-se com os operadores padrão - chegando ao que você já tem na pergunta.fonte
similar
uma verificação?EXPLAIN ANALYZE
mostra 2 varreduras de índice de bitmap. Várias verificações de índice de bitmap podem ser combinadas rapidamente.OR
comUNION ALL
ou substituirname LIKE 'B%'
comname >= 'B' AND name <'C'
no Postgres?UNION
não, mas sim, combinar os intervalos em umaWHERE
cláusula acelerará a consulta. Eu adicionei mais à minha resposta. Obviamente, você deve levar seu código de idioma em consideração. A pesquisa com reconhecimento de localidade é sempre mais lenta.Que tal adicionar uma coluna à tabela. Dependendo dos seus requisitos reais:
O PostgreSQL não suporta colunas computadas em tabelas base no SQL Server, mas a nova coluna pode ser mantida por meio de gatilho. Obviamente, essa nova coluna seria indexada.
Como alternativa, um índice em uma expressão forneceria o mesmo, mais barato. Por exemplo:
As consultas que correspondem à expressão em suas condições podem utilizar esse índice.
Dessa forma, o resultado do desempenho é obtido quando os dados são criados ou alterados; portanto, pode ser apropriado apenas para um ambiente de baixa atividade (ou seja, muito menos gravações do que leituras).
fonte
Você poderia tentar
Não tenho idéia se a expressão acima ou sua expressão original é sargável no Postgres.
Se você criar o índice sugerido, também ficaria interessado em saber como isso se compara com as outras opções.
fonte
O que eu fiz no passado, diante de um problema de desempenho semelhante, é incrementar o caractere ASCII da última letra e fazer ENTRE OS. Você obtém o melhor desempenho, para um subconjunto da funcionalidade LIKE. Obviamente, ele só funciona em determinadas situações, mas para conjuntos de dados muito grandes, nos quais você procura um nome, por exemplo, faz com que o desempenho passe de péssimo para aceitável.
fonte
Pergunta muito antiga, mas encontrei outra solução rápida para esse problema:
Como a função ascii () olha apenas para o primeiro caractere da string.
fonte
(name)
?Para verificar as iniciais, costumo usar a conversão para
"char"
(com aspas duplas). Não é portátil, mas muito rápido. Internamente, ele simplesmente desativa o texto e retorna o primeiro caractere, e as operações de comparação "char" são muito rápidas porque o tipo tem comprimento fixo de 1 byte:Observe que a conversão para to
"char"
é mais rápida que aascii()
slution de @ Sole021, mas não é compatível com UTF8 (ou qualquer outra codificação), retornando simplesmente o primeiro byte; portanto, só deve ser usado nos casos em que a comparação for anterior à simples 7 caracteres ASCII de bits.fonte
Existem dois métodos ainda não mencionados para lidar com esses casos:
índice parcial (ou particionado - se criado para todo o intervalo manualmente) - mais útil quando apenas um subconjunto de dados é necessário (por exemplo, durante alguma manutenção ou temporário para alguns relatórios):
particionando a própria tabela (usando o primeiro caractere como chave de particionamento) - vale a pena considerar esta técnica no PostgreSQL 10+ (particionamento menos doloroso) e 11+ (remoção da partição durante a execução da consulta).
Além disso, se os dados em uma tabela são classificados, pode-se beneficiar do uso do índice BRIN (sobre o primeiro caractere).
fonte
Provavelmente mais rápido para fazer uma comparação de caracteres únicos:
fonte
column LIKE 'B%'
será mais eficiente do que usar a função de substring na coluna.