BまたはDで始まる人の名前を探す単純なクエリを作成する必要がありました。
SELECT s.name
FROM spelers s
WHERE s.name LIKE 'B%' OR s.name LIKE 'D%'
ORDER BY 1
パフォーマンスを向上させるためにこれを書き換える方法があるかどうか疑問に思っていました。だから私は避けることができますor
/またはlike
?
name
、パフォーマンスを重視する場合、ここでインデックスを使用すると便利です。
BまたはDで始まる人の名前を探す単純なクエリを作成する必要がありました。
SELECT s.name
FROM spelers s
WHERE s.name LIKE 'B%' OR s.name LIKE 'D%'
ORDER BY 1
パフォーマンスを向上させるためにこれを書き換える方法があるかどうか疑問に思っていました。だから私は避けることができますor
/またはlike
?
name
、パフォーマンスを重視する場合、ここでインデックスを使用すると便利です。
回答:
クエリはほぼ最適です。構文はそれほど短くなりませんし、クエリはそれほど速くなりません:
SELECT name
FROM spelers
WHERE name LIKE 'B%' OR name LIKE 'D%'
ORDER BY 1;
本当に構文を短縮したい場合は、ブランチを含む正規表現を使用します。
...
WHERE name ~ '^(B|D).*'
または、文字クラスを使用してわずかに高速化:
...
WHERE name ~ '^[BD].*'
インデックスなしのクイックテストはSIMILAR TO
、どちらの場合よりも高速な結果をもたらします。
適切なBツリーインデックスが適切に配置されていれば、LIKE
このレースで桁違いに勝ちます。
マニュアルのパターンマッチングの基本を読んでください。
パフォーマンスに関心がある場合は、大きなテーブル用に次のようなインデックスを作成します。
CREATE INDEX spelers_name_special_idx ON spelers (name text_pattern_ops);
この種のクエリを桁違いに高速化します。ロケール固有のソート順には特別な考慮事項が適用されます。マニュアルの演算子クラスの詳細をお読みください。標準の "C"ロケールを使用している場合(ほとんどの人はそうではありません)、プレーンインデックス(既定の演算子クラスを使用)でできます。
このようなインデックスは、左アンカーパターン(文字列の先頭からの一致)にのみ適しています。
SIMILAR TO
または、基本的な左アンカー式の正規表現もこのインデックスを使用できます。ただし、ブランチまたは文字クラスは使用しません(少なくともPostgreSQL 9.0のテストでは)。(B|D)
[BD]
Trigramの一致またはテキスト検索では、特別なGINまたはGiSTインデックスが使用されます。
LIKE
(~~
)はシンプルで高速ですが、機能に制限があります。
ILIKE
(~~*
)大文字と小文字を区別しないバリアント。
pg_trgmは両方のインデックスサポートを拡張します。
~
(正規表現の一致)は強力ですが、より複雑であり、基本的な表現以外のものでは遅くなる場合があります。
SIMILAR TO
ただで無意味。特異なハーフブリードLIKE
と正規表現。私は決して使いません。下記参照。
%は、追加モジュールによって提供される「類似性」演算子ですpg_trgm
。下記参照。
@@
テキスト検索演算子です。下記参照。
以降ではPostgreSQL 9.1あなたは、拡張を容易にすることができるpg_trgm
インデックスのためのサポートを提供するためにあらゆる LIKE
/ ILIKE
パターン(ととの簡単な正規表現パターンを~
GINか、GiSTインデックスを使用して)。
詳細、例、リンク:
pg_trgm
これらの演算子も提供します:
%
-「類似性」演算子 <%
(交換子:%>
)-Postgres 9.6以降の「word_similarity」演算子<<%
(交換子:%>>
)-Postgres 11以降の「strict_word_similarity」演算子個別のインフラストラクチャおよびインデックスタイプとの特別なタイプのパターンマッチングです。辞書とステミングを使用し、特に自然言語の場合に文書内の単語を検索するための優れたツールです。
プレフィックスマッチングもサポートされています。
だけでなく、フレーズ検索のPostgres 9.6以降:
マニュアルの概要と、演算子と機能の概要を検討してください。
追加のモジュールfuzzystrmatchにはさらにいくつかのオプションがありますが、パフォーマンスは一般的に上記のすべてより劣っています。
特に、levenshtein()
関数のさまざまな実装が役立つ場合があります。
~
)は常により速いのですSIMILAR TO
か?答えは簡単です。SIMILAR TO
式は内部的に正規表現に書き換えられます。したがって、すべてのSIMILAR TO
式には、少なくとも 1つの高速な正規表現があります(式を書き換えるオーバーヘッドを節約します)。SIMILAR TO
これまでに使用してもパフォーマンスは向上しません。
とにかくLIKE
(~~
)で実行できる簡単な式はより高速LIKE
です。
SIMILAR TO
SQL標準の初期のドラフトで終わったため、PostgreSQLでのみサポートされています。彼らはまだそれを取り払っていない。しかし、それを削除し、代わりに正規表現の一致を含める計画があります-またはそう聞いた。
EXPLAIN ANALYZE
それを明らかにします。自分でテーブルを試してみてください!
EXPLAIN ANALYZE SELECT * FROM spelers WHERE name SIMILAR TO 'B%';
明らかに:
...
Seq Scan on spelers (cost= ...
Filter: (name ~ '^(?:B.*)$'::text)
SIMILAR TO
正規表現(~
)で書き換えられました。
しかし、EXPLAIN ANALYZE
より多くを明らかにする。上記のインデックスを設定して試してください:
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))
内部的には、ロケールに対応していないインデックス(とtext_pattern_ops
や使用してロケールC
:)シンプルな左アンカー表現は、これらのテキストパターン演算子を使用して書き換えられ~>=~
、~<=~
、~>~
、~<~
。これは~
、~~
などSIMILAR TO
です。
またはを持つvarchar
型のインデックスについても同様です。varchar_pattern_ops
char
bpchar_pattern_ops
したがって、元の質問に適用すると、これは可能な限り速い方法です:
SELECT name
FROM spelers
WHERE name ~>=~ 'B' AND name ~<~ 'C'
OR name ~>=~ 'D' AND name ~<~ 'E'
ORDER BY 1;
もちろん、隣接するイニシャルを検索する必要がある場合は、さらに単純化できます。
WHERE name ~>=~ 'B' AND name ~<~ 'D' -- strings starting with B or C
~
またはを単純に使用する場合の~~
利点はわずかです。パフォーマンスが最重要要件ではない場合は、標準の演算子に固執する必要があります-既に質問にあるものに到達します。
similar
スキャンが含まれることを知っていますか?
EXPLAIN ANALYZE
2つのビットマップインデックススキャンを示す簡単なテスト。複数のビットマップインデックススキャンをかなり迅速に組み合わせることができます。
OR
とUNION ALL
か、交換するname LIKE 'B%'
と name >= 'B' AND name <'C'
Postgresのでは?
UNION
ませんが、はい、範囲を1つのWHERE
句に結合するとクエリが高速化されます。回答にさらに追加しました。もちろん、ロケールを考慮する必要があります。ロケール対応の検索は常に低速です。
テーブルに列を追加する方法について。実際の要件に応じて:
person_name_start_with_B_or_D (Boolean)
person_name_start_with_char CHAR(1)
person_name_start_with VARCHAR(30)
PostgreSQLはSQL Serverの基本テーブルの計算列をサポートしていませんが、トリガーを介して新しい列を維持できます。明らかに、この新しい列にはインデックスが付けられます。
または、式のインデックスを使用すると、同じように安くなります。例えば:
CREATE INDEX spelers_name_initial_idx ON spelers (left(name, 1));
条件の式に一致するクエリは、このインデックスを利用できます。
この方法では、データが作成または修正されたときにパフォーマンスヒットが発生するため、アクティビティの少ない環境(つまり、読み取りより書き込みがはるかに少ない環境)にのみ適しています。
試すことができます
SELECT s.name
FROM spelers s
WHERE s.name SIMILAR TO '(B|D)%'
ORDER BY s.name
しかし、上記の式または元の式のいずれかがPostgresで検索可能かどうかはわかりません。
提案されたインデックスを作成する場合、これが他のオプションとどのように比較されるかについても興味があります。
SELECT name
FROM spelers
WHERE name >= 'B' AND name < 'C'
UNION ALL
SELECT name
FROM spelers
WHERE name >= 'D' AND name < 'E'
ORDER BY name
非常に古い質問ですが、この問題の別の迅速な解決策を見つけました。
SELECT s.name
FROM spelers s
WHERE ascii(s.name) in (ascii('B'),ascii('D'))
ORDER BY 1
関数ascii()は文字列の最初の文字のみを調べるため。
(name)
か?
イニシャルの確認には、"char"
(二重引用符で)キャストすることがよくあります。ポータブルではありませんが、非常に高速です。内部的には、単純にテキストをデトーストし、最初の文字を返します。タイプが1バイト固定長であるため、「char」比較操作は非常に高速です。
SELECT s.name
FROM spelers s
WHERE s.name::"char" =ANY( ARRAY[ "char" 'B', 'D' ] )
ORDER BY 1
へのキャスト"char"
はascii()
@ Sole021によるスリューションよりも高速ですが、UTF8互換(またはその他のエンコード)ではなく、単に最初のバイトを返すため、比較がプレーン7と比較される場合にのみ使用してください。ビットASCII文字。
そのような場合に対処するための、まだ言及されていない2つの方法があります。
部分(またはパーティション-手動でフルレンジ用に作成された場合)インデックス-データのサブセットのみが必要な場合(たとえば、メンテナンス中またはレポートの一時的な場合)に最も便利です。
CREATE INDEX ON spelers WHERE name LIKE 'B%'
テーブル自体のパーティショニング(パーティショニングキーとして最初の文字を使用)-この手法は、PostgreSQL 10+(苦痛の少ないパーティショニング)および11+(クエリ実行中のパーティションプルーニング)で特に考慮する価値があります。
さらに、テーブル内のデータがソートされている場合、BRINインデックス(最初の文字)を使用することでメリットが得られます。
おそらく、1文字の比較をより高速に実行します。
SUBSTR(s.name,1,1)='B' OR SUBSTR(s.name,1,1)='D'
column LIKE 'B%'
列で部分文字列関数を使用するよりも効率的です。
s.name
インデックス化?