1

シナリオ:データベースはsqliteです(データベース内のレコードを暗号化する必要があります。したがって、iOS用のSQL暗号化APIを使用します)

データベースには、次のようなスキーマを持つpartnumberという名前のテーブルがあります。

CREATE TABLE partnumber (
  objid varchar PRIMARY KEY,
  description varchar,
  make varchar,
  model varcha,
  partnumber varchar,
  SSOKey varchar,
  PMOKey varchar
)

このテーブルには、約80Kのレコードが含まれています。

UIビューには3つのテキストフィールドがあり、ユーザーは検索語を入力でき、ユーザーがそこに文字を入力するとすぐに検索が行われます。

3つのテキストフィールドは、txtFieldDescription、txtFieldMake、およびtxtFieldModelです。

最初のユーザーがtxtFieldDescriptionに「monitor」として検索語を入力するとします。したがって、各文字で実行されるクエリは次のとおりです。

1.1。

    SELECT DISTINCT description COLLATE NOCASE
    FROM partnumber where description like ‘%m%’

2.2。

    SELECT DISTINCT description COLLATE NOCASE
    FROM partnumber where description like ‘%mo%’

3.3。

    SELECT DISTINCT description COLLATE NOCASE
    FROM partnumber where description like ‘%mon%’

4.4。

    SELECT DISTINCT description COLLATE NOCASE
    FROM partnumber where description like ‘%moni%’

5.5。

    SELECT DISTINCT description COLLATE NOCASE
    FROM partnumber where description like ‘%monit%’

6.6。

    SELECT DISTINCT description COLLATE NOCASE
    FROM partnumber where description like ‘%monito%’

7。

    SELECT DISTINCT description COLLATE NOCASE
    FROM partnumber where description like ‘%monitor%’

ここまでは順調ですね。ここで、ユーザーがモデルを検索したい場合を考えてみましょう(txtFieldDescriptionにはまだ「monitor」が含まれています)。したがって、ユーザーはtxtFieldModelをクリックします。ユーザーがモデルをクリックするとすぐに、クエリは次のように実行されます。

SELECT DISTINCT model COLLATE NOCASE
FROM partnumber where description like ‘%monitor%’

このクエリは、説明にモニターが含まれているレコードのすべてのモデルを返します(任意の位置)。

ここで、ユーザーが「sony」という単語を含むすべてのモデルを検索する場合(説明フィールドには引き続きモニターが含まれます)、各文字で実行されるクエリは次のとおりです。

1.1。

    SELECT DISTINCT model COLLATE NOCASE
    FROM partnumber WHERE model like ‘%s%’ AND description like ‘%monitor%’

2.2。

    SELECT DISTINCT model COLLATE NOCASE
    FROM partnumber WHERE model like ‘%so%’ AND description like ‘%monitor%’

3.3。

    SELECT DISTINCT model COLLATE NOCASE
    FROM partnumber WHERE model like ‘%son%’ AND description like ‘%monitor%’

4.4。

    SELECT DISTINCT model COLLATE NOCASE
    FROM partnumber WHERE model like ‘%sony%’ AND description like ‘%monitor%’

ここで、ユーザーがtxtFieldMakeをクリックし、検索語を「1980」と入力すると、実行されるクエリは次のようになります。

1.1。

    SELECT DISTINCT make COLLATE NOCASE
    FROM partnumber WHERE make like ‘%1%’
      AND model like ‘%sony%’ AND description like ‘%monitor%’

2.2。

    SELECT DISTINCT make COLLATE NOCASE
    FROM partnumber WHERE make like ‘%19%’
      AND model like ‘%sony%’ AND description like ‘%monitor%’

3.3。

    SELECT DISTINCT make COLLATE NOCASE
    FROM partnumber WHERE make like ‘%198%’
      AND model like ‘%sony%’ AND description like ‘%monitor%’

4.4。

    SELECT DISTINCT make COLLATE NOCASE
    FROM partnumber WHERE make like ‘%1980%’
      AND model like ‘%sony%’ AND description like ‘%monitor%’

ここで、txtFieldDescriptionからtxtFieldModelまたはtxtFieldModelからtxtFieldMakeへの移行の時間遅延が大きすぎ、txtFieldModelおよびtxtFieldMakeでは、入力された文字が5秒または6秒後に表示されるため(クエリが処理された後)、カーソルがそこでハングします。 。

分析すると、likeキーワード('%monitor%'など)の検索語の前のワイルドカードによって実行が遅くなることがわかりました。また、この場合、ANDを挟んだようなキーワードが3つもある可能性があるため、実行時間は確実に長くなります。また、likeの先頭でワイルドカードを使用すると、インデックスが無効になります。

いくつかの追加情報:

  1. レコードの総数〜80K

  2. SELECTクエリは、テーブルの部品番号(〜80K)に対して毎回実行されます。

  3. 私が実行したいくつかのクエリの結果:

    Sqlite> SELECT count(DISTINCT description COLLATE NOCASE) from partnumber;
        Result is: 2599
    
    Sqlite> SELECT count(DISTINCT make COLLATE NOCASE) from partnumber;
        Result is: 7129
    
    Sqlite> SELECT count(DISTINCT model COLLATE NOCASE) from partnumber;
        Result is: 64644
    
    Sqlite> SELECT count(objid) from partnumber;
        Result is: 82135
    
  4. インデックスは次のように作成されます。

    CREATE INDEX index_description
    ON partnumber (description collate nocase)
    
    CREATE INDEX index_make
    ON partnumber (make collate nocase)
    
    CREATE INDEX index_model
    ON partnumber (model collate nocase)
    

パフォーマンスを向上させるためのいくつかの代替案:

  1. 個別記述の数は2599のみで、makeの数は7129のみであるため、テーブルは、DISTINCT description COLLATE NOCASE出力(合計2599行)を含むテーブルとDISTINCT make COLLATE NOCASE(合計7129行)。モデルに関する限り、行数〜64644は合計レコード数〜82135にほぼ等しいため、別のテーブルを作成しても役に立ちません。ただし、このアプローチの問題は、これらのテーブルをどのように検索するか、各テーブルにどの列が必要か、テーブルをいくつ作成する必要があるかがわからないことです。ユーザーが説明を入力してからモデルを入力し、次に新しい説明を入力するとどうなりますか。

  2. この選択クエリの結果はUITableViewに表示されており、ユーザーには一度に最大5行が表示されるためです。したがって、返される行数を500に制限でき、ユーザーがスクロールすると、最後に検索されたレコードまで次の500をフェッチできます。

ただし、ここでの問題は、500レコードしか必要ないにもかかわらず、テーブル全体を検索する必要があることです(SCAN〜80Kレコード)。したがって、最初にテーブルの上位10%のみを検索し、これから上位500行を返すクエリが必要です。次に、上位10%のレコードがすべて検索され、次の10%、次の10%から80000のレコードが検索されます。検索されています(10〜10%のレコードのチャンクで検索する必要があります)。

  1. 80Kレコードのテーブルをそれぞれ20Kレコードの4つのテーブルに分割し、4つのテーブルすべてに対して同時に(異なるバックグラウンドスレッドで)検索を実行して、結果セットを取得できる場合。しかし、ここでは、4つの異なるスレッド(バッチ実行の一種)でクエリを実行する方法、結果を組み合わせるタイミング、およびすべてのスレッドが実行を終了したことを知る方法がわかりません。

  2. %monitor%'のように、同じ結果を返すが実行が速く、その関数の使用がインデックスの使用に影響を与えない(つまり、インデックスの使用をバイパスしない)別の関数に置き換えることができる場合、その後、実行が速くなる可能性があります。誰かがsqliteでそのような関数を私に提案できるなら、私はこのアプローチを続けることができます。

これらの選択肢のいずれかを実装するのを手伝ってくれる場合、または他の解決策を提案してくれる場合は、クエリの実行速度を上げることができます。そして、私はすでにこれを試したので、plsはsqliteでFTS(全文検索)を有効にするように教えてくれませんが、正確な手順はわかりません。この質問を辛抱強く読んでくれてありがとう......

編集:

やあみんな、私はいくつかの成功を得ました。選択クエリを次のように変更しました。

select distinct description collate nocase as description from partnumber where rowid BETWEEN 1 AND (select max(rowid) from partnumber) AND description like '%a%' order by description;

そしてビンゴ、検索時間はかつてないほどでした。しかし、問題は、このようにコマンドEXPLAIN QUERY PLANを実行すると、使用したくない個別のBツリーを使用していることを示しています。

explain query plan select distinct description collate nocase as description from partnumber where rowid BETWEEN 1 AND (select max(rowid) from partnumber) AND description like '%a%' order by description;

出力:

0|0|0|SEARCH TABLE partnumber USING INTEGER PRIMARY KEY (rowid>? AND rowid<?) (~15625 rows)
0|0|0|EXECUTE SCALAR SUBQUERY 1
1|0|0|SEARCH TABLE partnumber USING INTEGER PRIMARY KEY (~1 rows)
0|0|0|USE TEMP B-TREE FOR DISTINCT

編集:

すみません。上記のアプローチ(検索にrowidを使用)は、元のアプローチよりもデバイスで時間がかかります。キーワードによる区別と順序を削除しようとしましたが、役に立ちませんでした。iPhoneではまだ8〜10秒かかります。plsは私を助けます。

4

1 に答える 1

0

アンシュル、

「sqliteでFTS(全文検索)を有効にするように言わないでください。これはすでに試したことがありますが、正確な手順はわかりません」とあなたが言ったことは知っていますが、FTSはこれを実行する唯一の方法です良い。テーブル全体のスキャンを適切に実行する魔法はありません。FTS を読み、時間をかけて学習し、それを使用することをお勧めします: http://sqlite.org/fts3.html

于 2012-03-07T20:41:48.537 に答える