La tua query è praticamente ottimale. La sintassi non sarà molto più breve, la query non sarà molto più veloce:
SELECT name
FROM spelers
WHERE name LIKE 'B%' OR name LIKE 'D%'
ORDER BY 1;
Se vuoi davvero accorciare la sintassi , usa un'espressione regolare con rami :
...
WHERE name ~ '^(B|D).*'
O leggermente più veloce, con una classe di caratteri :
...
WHERE name ~ '^[BD].*'
Un test rapido senza indice produce risultati più rapidi rispetto a quelli SIMILAR TO
in entrambi i casi per me.
Con un indice B-Tree appropriato, LIKE
vince questa gara per ordini di grandezza.
Leggi le nozioni di base sulla corrispondenza dei modelli nel manuale .
Indice per prestazioni superiori
Se sei interessato alle prestazioni, crea un indice come questo per tabelle più grandi:
CREATE INDEX spelers_name_special_idx ON spelers (name text_pattern_ops);
Rende più veloce questo tipo di query per ordini di grandezza. Considerazioni speciali si applicano all'ordinamento specifico della locale. Maggiori informazioni sulle classi di operatori nel manuale . Se si utilizza la locale "C" standard (la maggior parte delle persone non lo fa), lo farà un indice semplice (con classe operatore predefinita).
Un tale indice è valido solo per i motivi ancorati a sinistra (corrispondenti dall'inizio della stringa).
SIMILAR TO
oppure anche espressioni regolari con espressioni di base ancorate a sinistra possono utilizzare questo indice. Ma non con rami (B|D)
o classi di caratteri [BD]
(almeno nei miei test su PostgreSQL 9.0).
Le corrispondenze trigramma o la ricerca di testo utilizzano indici GIN o GiST speciali.
Panoramica degli operatori di corrispondenza dei modelli
LIKE
( ~~
) è semplice e veloce ma limitato nelle sue capacità.
ILIKE
( ~~*
) la variante insensibile al maiuscolo / minuscolo.
pg_trgm estende il supporto dell'indice per entrambi.
~
(corrispondenza di espressioni regolari) è potente ma più complesso e può essere lento per qualcosa di più delle espressioni di base.
SIMILAR TO
è semplicemente inutile . Un singolare ibrido di LIKE
ed espressioni regolari. Non lo uso mai. Vedi sotto.
% è l'operatore di "somiglianza", fornito dal modulo aggiuntivopg_trgm
. Vedi sotto.
@@
è l'operatore di ricerca del testo. Vedi sotto.
pg_trgm - corrispondenza trigramma
A partire da PostgreSQL 9.1 è possibile facilitare l'estensione pg_trgm
per fornire supporto all'indice per qualsiasi LIKE
/ ILIKE
pattern (e semplici schemi regexp con ~
) utilizzando un indice GIN o GiST.
Dettagli, esempio e collegamenti:
pg_trgm
fornisce inoltre questi operatori :
%
- l'operatore di "somiglianza"
<%
(commutatore %>
:) - l'operatore "word_similarity" in Postgres 9.6 o successivo
<<%
(commutatore %>>
:) - l'operatore "strict_word_similarity" in Postgres 11 o successivo
Ricerca di testo
È un tipo speciale di pattern matching con infrastrutture e tipi di indice separati. Utilizza dizionari e stemming ed è un ottimo strumento per trovare parole nei documenti, specialmente per le lingue naturali.
La corrispondenza dei prefissi è inoltre supportata:
Oltre alla ricerca di frasi da Postgres 9.6:
Considerare l' introduzione nel manuale e la panoramica degli operatori e delle funzioni .
Strumenti aggiuntivi per la corrispondenza delle stringhe fuzzy
Il modulo aggiuntivo fuzzystrmatch offre alcune opzioni in più, ma le prestazioni sono generalmente inferiori a tutto quanto sopra.
In particolare, varie implementazioni della levenshtein()
funzione possono essere strumentali.
Perché le espressioni regolari ( ~
) sono sempre più veloci di SIMILAR TO
?
La risposta è semplice SIMILAR TO
le espressioni vengono riscritte internamente in espressioni regolari. Quindi, per ogni SIMILAR TO
espressione, esiste almeno un'espressione regolare più veloce (che consente di risparmiare il sovraccarico di riscrivere l'espressione). Non si ottiene SIMILAR TO
mai un miglioramento delle prestazioni nell'uso .
E le espressioni semplici che possono essere fatte con LIKE
( ~~
) sono LIKE
comunque più veloci .
SIMILAR TO
è supportato solo in PostgreSQL perché è finito nelle prime bozze dello standard SQL. Non se ne sono ancora liberati. Ma ci sono piani per rimuoverlo e includere invece corrispondenze regexp - o almeno così ho sentito.
EXPLAIN ANALYZE
lo rivela. Prova tu stesso con qualsiasi tavolo!
EXPLAIN ANALYZE SELECT * FROM spelers WHERE name SIMILAR TO 'B%';
rivela:
...
Seq Scan on spelers (cost= ...
Filter: (name ~ '^(?:B.*)$'::text)
SIMILAR TO
è stato riscritto con un'espressione regolare ( ~
).
Massime prestazioni per questo caso particolare
Ma EXPLAIN ANALYZE
rivela di più. Prova, con l'indice di cui sopra in atto:
EXPLAIN ANALYZE SELECT * FROM spelers WHERE name ~ '^B.*;
rivela:
...
-> 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, con un indice che non è informativi sulla localizzazione ( text_pattern_ops
o utilizzando impostazioni locali C
) semplici espressioni di sinistra-ancorata vengono riscritti con questi operatori modello di testo: ~>=~
, ~<=~
, ~>~
, ~<~
. Questo è il caso per ~
, ~~
o SIMILAR TO
simili.
Lo stesso vale per gli indici sui varchar
tipi con varchar_pattern_ops
o char
con bpchar_pattern_ops
.
Quindi, applicato alla domanda originale, questo è il modo più veloce possibile :
SELECT name
FROM spelers
WHERE name ~>=~ 'B' AND name ~<~ 'C'
OR name ~>=~ 'D' AND name ~<~ 'E'
ORDER BY 1;
Naturalmente, se dovessi cercare iniziali adiacenti , puoi semplificare ulteriormente:
WHERE name ~>=~ 'B' AND name ~<~ 'D' -- strings starting with B or C
Il guadagno rispetto all'uso semplice ~
o ~~
è minimo. Se le prestazioni non sono il tuo requisito fondamentale, dovresti semplicemente attenerti agli operatori standard, arrivando a ciò che hai già nella domanda.
s.name
indicizzato?