Sua consulta é praticamente a ideal. A sintaxe não ficará muito mais curta, a consulta não ficará muito mais rápida:
SELECT name
FROM spelers
WHERE name LIKE 'B%' OR name LIKE 'D%'
ORDER BY 1;
Se você realmente deseja reduzir a sintaxe , use uma expressão regular com branches :
...
WHERE name ~ '^(B|D).*'
Ou um pouco mais rápido, com uma classe de personagem :
...
WHERE name ~ '^[BD].*'
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:
CREATE INDEX spelers_name_special_idx ON spelers (name text_pattern_ops);
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 peculiar LIKE
e expressões regulares. Eu nunca uso isso. Ver abaixo.
% é o operador "similaridade", fornecido pelo módulo adicionalpg_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ão LIKE
/ 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 posterior
Pesquisa 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 que SIMILAR TO
?
A resposta é simples. SIMILAR TO
expressões são reescritas em expressões regulares internamente. Portanto, para cada SIMILAR 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 usar SIMILAR TO
sempre .
E expressões simples que podem ser feitas com LIKE
( ~~
) são mais rápidas de LIKE
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!
EXPLAIN ANALYZE SELECT * FROM spelers WHERE name SIMILAR TO 'B%';
Revela:
...
Seq Scan on spelers (cost= ...
Filter: (name ~ '^(?:B.*)$'::text)
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:
EXPLAIN ANALYZE SELECT * FROM spelers WHERE name ~ '^B.*;
Revela:
...
-> Bitmap Heap Scan on spelers (cost= ...
Filter: (name ~ '^B.*'::text)
-> Bitmap Index Scan on spelers_name_text_pattern_ops_idx (cost= ...
Index Cond: ((prod ~>=~ 'B'::text) AND (prod ~<~ 'C'::text))
Internamente, com um índice que não está locale-aware ( text_pattern_ops
ou usando local C
) expressões simples ancorado-esquerda são reescritas com estes operadores padrão de texto: ~>=~
, ~<=~
, ~>~
, ~<~
. Este é o caso ~
, ~~
ou SIMILAR TO
similar.
O mesmo vale para índices em varchar
tipos com varchar_pattern_ops
ou char
com bpchar_pattern_ops
.
Portanto, aplicada à pergunta original, esta é a maneira mais rápida possível :
SELECT name
FROM spelers
WHERE name ~>=~ 'B' AND name ~<~ 'C'
OR name ~>=~ 'D' AND name ~<~ 'E'
ORDER BY 1;
Obviamente, se você procurar iniciais adjacentes , poderá simplificar ainda mais:
WHERE name ~>=~ 'B' AND name ~<~ 'D' -- strings starting with B or C
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.
s.name
indexado?