Your query is pretty much the optimum. Syntax won't get much shorter, query won't get much faster. I only reformat:
WHERE name ~~ 'B%' OR name ~~ 'D%'
ORDER BY 1;
If you really want to shorten the syntax a bit, use a regular expression with branches:
WHERE name ~ '^(B|D).*'
Or slightly faster, yet, with a character class:
WHERE name ~ '^[BD].*'
A quick test yields faster results than for
SIMILAR TO in both cases for me. Either is also slightly faster than two
LIKE conditions (One scan with an expensive condition is still slightly cheaper than two scans with a cheap condition.)
SIMILAR TO is a peculiar halfbreed of
LIKE and regular expressions that's only included in PostgreSQL because it is in an SQL standard. I hardly ever use it myself. More about that further down.
Index for superior performance
If you are concerned with performance, create an index like this:
CREATE INDEX spelers_name_text_pattern_ops_idx
ON spelers (name text_pattern_ops);
Will speed up this kind of query by orders of magnitude in bigger tables. Special considerations apply for locale-specific sort order. Read more about operator classes in the manual. If you are using the standard "C" locale (most people don't), then a plain index will do.
Note, that such an index cannot be utilized for a full-text search. Only patterns anchored at the start will use it.
Note also, that
SIMILAR TO or regular expressions with branches
(B|D) or character classes
[BD] in the pattern will also not use the index (at least in my tests on PostgreSQL 9.0).
Read about pattern matching in the manual.
Beginning with PostgreSQL 9.1 you can facilitate the extension
pg_trgm to provide index support for any LIKE / ILIKE pattern with a GIN or GiST index.
Details, example and links in this related answer.
pg_trgm also provides the "similarity" operator '%', which may be interesting in the context of pattern matching.
Why are regular expressions (
~) faster than
The answer is simple.
SIMILAR TO expressions are rewritten into regular expressions internally. So, for every
SIMILAR TO expression, there is at least one faster regular expression (that saves the overhead of rewriting the expression). There is no performance gain in using
SIMILAR TO ever.
And simple expressions that can be done with
~~) are faster with
EXPLAIN ANALYZE reveals it. Just try with any table yourself!
EXPLAIN ANALYZE SELECT * FROM spelers WHERE name SIMILAR TO 'B%';
Seq Scan on spelers (cost= ...
Filter: (name ~ '^(?:B.*)$'::text)
SIMILAR TO has been rewritten with a regular expression (
EXPLAIN ANALYZE reveals more. Try, with the afore-mentioned index in place:
EXPLAIN ANALYZE SELECT * FROM spelers WHERE name ~ '^B.*;
-> 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))
Internally, with an index that is not locale-aware (
text_pattern_ops or using locale
C) simple left-anchored expressions are rewritten with these text pattern operators:
~<~. This is the case for
SIMILAR TO alike.
The same is also the case for indexes on
varchar types with
So, applied to the original question, this is the fastest possible way:
WHERE name ~>=~ 'B' AND name ~<~ 'C'
OR name ~>=~ 'D' AND name ~<~ 'E'
ORDER BY 1;
Of course, if you should happen to search for adjacent Initials (Martin's question in the comments), you can further simplify:
WHERE name ~>=~ 'B' AND name ~<~ 'D' -- strings starting with B or C
The gain over plain use of
~~ is small. If performance isn't your paramount requirement, you might just stick with the standard operators - arriving at what you already have in the question.