質問

私は Windows Mobile 6 で実行されるアプリケーションに取り組んでいます。このアプリケーションでは、アイテムの説明フィールド内に特定の文字列 (エンド ユーザーが提供した) を含むアイテム テーブルからすべてのアイテムを取得できる必要があります。問題は、テーブルに約 170,000 個の項目があることです。説明内の任意の場所に文字列を含むすべての項目を返す必要があるため、LIKE %string% を使用する必要があります。これにより、インデックスを使用する機会がなくなります。データとテーブル構造はもともと Progress データベースに基づいており、単語のインデックスが付けられたフィールドに対して素晴らしい contains 演算子を備えています。モバイル アプリケーションでは SQL Server Compact 3.5 が使用されているため、これは当てはまりません。

基本的に、私の DAL はクエリを実行して SqlCeDataReader を取得し、次に、ItemFactory を使用して、一致した項目のみを含む List オブジェクトを作成します。これにより、明らかにドメイン/ビジネス オブジェクトをデータ アクセス層から分離できるようになります。

説明に「ゴルフ」などの文字が含まれるすべてのアイテムを検索するときに、アイテムを取得するのに 8 分と 42 秒かかることを除けば、素晴らしくて素敵です。明らかに、これはエンド ユーザーにとって許容できる時間枠ではありません。

私の最初の試みは、代わりに SELECT * FROM item" (メインのインデックス付きフィールドの 1 つに order by 句を使用) を使用して、データベースからすべてのアイテムを取得することでした。この時点で、SqlCeDataReader を実行するときに IndexOf チェックを実行し、要求された説明テキストが含まれている場合にのみ、ItemFactory に項目を List オブジェクトに追加させるようにしました。これにより速度は1分46秒まで短縮されました。みすぼらしいほどではありませんが、それでも遅すぎます。

次に、有望な別のアプローチを試してみました...ほとんど...アプリケーションの起動中に、データベース内のすべてのアイテム オブジェクトを含むリストを作成しようとしました (クエリを実行してリスト全体を設定するには約 2 分かかりますが、アプリが初期化されているため、少なくとも 1 回だけです...)まだ...うーん)。リストが完成したら、そのリストに対して次のようなクエリを簡単に実行できます (構文が正しいことを願っています...)私は現在仕事中ではないので、座っている PC には Visual Studio がありません):

List<Item> specificItems = 
    AllItems.FindAll(i => i.Description.IndexOf(searchString, StringComparison.OrdinalIgnoreCase) >= 0);

このアプローチにより、タイムは 21 秒まで短縮されました。非常に素晴らしいです(全体的にはまだ遅いですが)。ただし、問題は、データベースからすべての項目をロードするとメモリ使用量が多すぎることです。OutOfMemoryException がスローされていたため、初期ロード中に実際に最後の 20,000 項目を削除する必要がありました (つまり、21 秒の時間枠はおそらく 25 秒に近かったでしょう)。エミュレータのメモリ マネージャによると、まだ 20 MB の空き RAM があったのですが、プロセスに関連付けられるのは 32 MB または RAM だけだと聞いたことがあります (WM 6 に当てはまるかどうかはわかりませんが、どうやらそれで)。

これは、すべての項目を保持するために List オブジェクトを使用していたためではないことを確認するために (動的なサイズ変更を避けるために、コンストラクターで必要な容量を使用してインスタンス化していました)、これは、追加のメモリ使用量が発生する可能性があることも読みました。暗黙的に EnsureCapacity を呼び出します。Item[] 配列 (事前にサイズ設定) を使用してみました。これでもまだメモリの問題があり、サイズの差は無視できました。

さて、とりとめのない話はこれくらいにしておきます。データリーダーによってデータベースから返されるレコードを (異なる種類のフィールドでのインデックス検索を通じて) ある程度制限する必要がある可能性が高いことはわかっています。その後、最大のパフォーマンスを得るために、項目のより小さいサブセットに対して IndexOf を使用する可能性があります。 (したがって、Like 演算子をすべてスキップします)。ただし、これによりエンド ユーザーは単なる説明検索以上のものを入力する必要があります (おそらく、検索対象のアイテムの種類を制限するためのアイテム階層情報)。

何か案は?私のやり方は間違っているのでしょうか?

聞いていただきありがとうございます(この投稿は長くなってしまい、ちょっと大声で考えているので申し訳ありません)。

ああ、私が使用しているものを(要約として)追加する必要があります。

  • ウィンドウズモバイル6
  • SQL Server コンパクト エディション 3.5
  • C# 3.5

アップデート:以下で説明するブルーム フィルターのアプローチは興味深いように思えましたが、1 つの要件を満たすことができませんでした (上記では特に指定しませんでした)。他の単語の中に含まれる単語は実際には一致できません (例:「club」は「club」を返しません)。このため、私はまったく異なるアプローチを使用することを余儀なくされました (ケント・フレドリック...)ご指摘いただきありがとうございます)。彼のアプローチがほとんどの要件を満たすものであったため、私はケントの答えを正しいものとしてマークしました(ミッチ、あなたの答えは、ジャアンダーが提案したブルームフィルターと同様の問題を抱えていました)。しかし、私は彼のやり方とは(今のところ...)異なるアプローチをとりました。

私がやったことは、項目番号と説明のみを含むすべての項目オブジェクトをメモリに取り込むことです (これによりメモリ制限内に保たれますが、それでも初期化に必要以上に時間がかかります...)マルチスレッド化と、アプリケーションの実行中にバックグラウンドでその情報をロードすることで、それを処理できると思います)。検索を実行するために、独自の contains ルーチンを作成しました。このルーチンは、2 つのポインターといくつかのループを使用して説明と必要な一致するテキストを実行するアンマネージド C# コードで作成されます。説明内のどこかで一致するものが見つかった場合は、項目番号を配列に追加します。すべてのアイテムが検索されると、新しいクエリがデータベースに戻り、一致するアイテム番号のみを取得します (整数フィールドのインデックスにより非常に高速です)。次に、それらの項目がすべての情報 (項目番号と説明だけでなく) を含むリストとして作成されます。操作全体には約 5 ~ 10 秒かかります (説明によって異なります)。現時点ではこれで十分です。

これをさらに最適化することを引き続き検討します (検索語の文字数を追跡できるかもしれません...商品説明に残っている文字数が必要なテキストよりも少ない場合、ループは次の商品に直接続行される可能性があります)。

ご提案は引き続き歓迎いたします。今のところ、私はケントの答えを私の質問に対して「最も正しい」とマークしました。

contains ルーチンの作成を手伝ってくれた Dolch に感謝します。

役に立ちましたか?

解決

に投票しました ミッチ・ウィートの 答えはわかりますが、有効性をテストするためのトリックがいくつかあります。

[char],[int] でいっぱいのテーブルがあることに関する私の大きな懸念は、特にこの新しいテーブルで %word% を使用する場合、無意味な文字列比較を大量に実行することになる可能性があることです。(重複していますが、検索エントリと一致しません)。

私はおそらく実験することを選択するでしょう

Words
-----
chars  | word_id 

WordsToEntry
------------
word_id | entry_id 

そして、データベースのオーバーヘッドがこの起こり得る問題を軽減する価値があるかどうかを確認してください(テストできません、申し訳ありません)

他のヒント

items テーブルを (1 回) 前処理して (そこに追加される新しいエントリはそれぞれ処理する必要があります)、次のような単語出現テーブルを作成してみてはどうでしょうか。

CREATE TABLE WordItemOccurance
(
    [Word] varchar(50) not null,

    ItemId int not null
        constraint FK_Items references ItemTable(ID)
)

すべての項目を反復処理し、個別の単語に分割し、見つかったエントリを出現テーブルに追加します。

[Word] でのクラスター化インデックスの作成と、ItemId でのアイテム テーブルへの結合は高速である必要があります。

ブルームフィルターを使ってみてはいかがでしょうか。

  1. ウィキペディア
  2. ブルームフィルターを使用します
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top