やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

SQLite3最適化 like, glob

 インデックスを使って検索する方法。

成果物

情報源

方法

 likeglobbetweenのようにインデックスを使って範囲検索できる。だが、それは限らた条件のときだけ。詳細は上記参照。注目すべきは以下。

  1. パターンがワイルドカードで始まらないこと(前方一致のときのみインデックスを使える
  2. glob演算子の場合、組込のbinary照合シーケンスを使用して列にインデックスを付ける必要がある
  3. like演算子の場合
    • case_sensitive_like
      • true: 組込のbinary照合シーケンスを使用して列のインデックスを作成する必要がある
      • false: 組込のnocase照合シーケンスを使用して列のインデックスを作成する必要がある
    • escapeオプションを使用する場合、エスケープ文字はASCII、またはUTF-8のシングルバイト文字でなければならない

やってみる

 最初に検索の要件を考える必要がある。たとえば大文字・小文字の区別をしない検索だけを要件にするなら以下。

create table T(A text collate nocase);
create index T_A on T(A);
insert into T values('1A');
insert into T values('11A');
insert into T values('11A1');
insert into T values('2A');
insert into T values('2B');
pragma case_sensitive_like = fasel;
select A from T where A like '_A';

 もし区別するパターンでも高速にしたいなら、そのインデックスを作成することになる。ただし、ほぼ同じデータが量産されることになる。必要なディスク容量が増えてしまう。よってそれは省略する。

 検索方法はlike句を使う。globは使わない。区別する検索しかできないためインデックスが使えないから。

 区別しないのでcollatenocaseにする。また、like句も区別しないよう以下のように設定する(デフォルト値だが念の為)。

PRAGMA case_sensitive_like = false;

 インデックスが使われているか確認する。前方一致のときのみ使える。

explain query plan select A from T where A like 'A_';
QUERY PLAN
`--SEARCH TABLE T USING COVERING INDEX T_A (A>? AND A<?)

 もちろん完全一致でも使える。だがlike句を使う意味がない。=比較と同じ。

explain query plan select A from T where A like 'A';
QUERY PLAN
`--SEARCH TABLE T USING COVERING INDEX T_A (A>? AND A<?)

 だが、後方一致、部分一致のときはインデックスが使われない。全表スキャンになってしまう。

explain query plan select A from T where A like '_A';
QUERY PLAN
`--SCAN TABLE T
explain query plan select A from T where A like '_A_';
QUERY PLAN
`--SCAN TABLE T

 なお、globはインデックスが一切使われない。インデックスのソート順がnocaseに準じているから。globbinary順しかサポートされておらず、その順に並んでいないインデックスは役に立たないため。

explain query plan select A from T where A glob 'A?';
QUERY PLAN
`--SEARCH TABLE T USING COVERING INDEX T_A (A>? AND A<?)
explain query plan select A from T where A glob '?A';
QUERY PLAN
`--SCAN TABLE T
explain query plan select A from T where A glob '?A?';
QUERY PLAN
`--SEARCH TABLE T USING COVERING INDEX T_A (A>? AND A<?)

 ところで、どう考えても最も使うのは「部分一致」だろう。それなのにインデックスが使えない。これは痛い。likeglobのためにインデックスを作っても費用対効果は低いかもしれない。どうせ遅いならもっと汎用性のあるregexpか、キーワードを増やすFTSでも使ったほうが良いかも?

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux

前回まで