私のWebサイトのオートコンプリート検索機能は、販売アイテムのモデル番号を含むvarcharフィールドを検索します。
このフィールドには1〜75文字の文字列を含めることができ、テーブルには400 000行が含まれます。文字列の先頭からのみ検索し、実行に約150〜250ミリ秒かかるクエリを考え出しましたが、許容範囲内ですが、上司がクエリに部分文字列を検索させ、クエリを3〜10倍遅くします(およそ1000-2000ms)。
私はJSフィドルを作成して、データがどのように見え、2つのクエリがどのように見えるかの例を示します。
http://sqlfiddle.com/#!6/9efa3/2/
テーブルにはすでにいくつかのインデックスがあります。このオートコンプリート検索フィールドを高速化するためのベストプラクティスは何ですか? (データベースバージョンはSQLSERVER 2008R2です)
これが私が扱っているデータの簡単な例です:
CREATE TABLE [Products](
[productid] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[model] [nvarchar](75) NOT NULL
CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED
(
[productid] ASC
));
insert into products values ('UMPX1AA0011 danish e-315 woot');
insert into products values ('P27y719VC');
insert into products values ('VG2y439m-LED');
insert into products values ('UMUyX165AAB01');
insert into products values ('U28y79VF');
insert into products values ('U52417HJ');
insert into products values ('VA25746M-LED WITH FLYING CORNERS');
insert into products values ('S19F350HNN 1pc california storage');
insert into products values ('VA211917A');
insert into products values ('PM2500X2');
insert into products values ('E22470SWHE');
insert into products values ('V22465WLYDP');
insert into products values ('I129LMH1HKC');
insert into products values ('OM5EN X 35 the new version');
insert into products values ('DLS3060WDB');
insert into products values ('PVW');
insert into products values ('LI23721S');
insert into products values ('V173516LBM');
insert into products values ('VX2376-SMHD-A');
insert into products values ('GUM5FX1AA1001');
insert into products values ('GPM300X11');
insert into products values ('GUM-WH6AA002');
insert into products values ('2435V5LSB');
insert into products values ('P2418HZ');
insert into products values ('Stylish sectional one of a kind y-5151');
これらは私が比較している2つのクエリです
--runs acceptably fast, about 100-250ms
select * from products where model like 'y-5151'+'%';
--takes too long, around 1000-2500ms
select * from products where model like '%' + 'y-5151' +'%'
私が行った解決策は、彼の以下の2つのブログ投稿で Aaron Bertrand によって提案されているように、すべてのモデル#サブストリングの前処理バージョンを含む「ハーフトリアングラム」テーブルを作成することです。
https://sqlperformance.com/2017/02/sql-indexes/seek-leading-wildcard-sql-server
https://sqlperformance.com/2017/02/sql-performance/follow-up-1-leading-wildcard-seeks
私にとっての解決策は、検索文字列で始まるモデルを探す新しいテーブルを作成することでした。各モデルはそのようにリストされています。たとえば、product_IDが51
、モデルは7500 Twin Bed
ID|Model
----------------
51|7500 Twin Bed
51|500 Twin Bed
51|00 Twin Bed
51|0 Twin Bed
51| Twin Bed
51|Twin Bed
51|win Bed
51|in Bed
51|n Bed
51| Bed
51|Bed
51|ed
51|d
これにより、完全なワイルドカード検索を行う必要がなくなり、単純なselect distinct id_product from products_dictionary where model like 'Twin%'
は必要な結果を返します。クエリの所要時間が100ミリ秒未満になりました。
これが、テーブルを作成してデータを設定するために使用したコードです。繰り返しますが、これはすべてAaronのブログ投稿で適切に説明されています。
CREATE TABLE [dbo].[products_dictionary](
[Id_product] [int],
[model] [nvarchar](75) NOT NULL
)
insert into [products_dictionary]
select p.id_product,f.fragment from products p
cross apply dbo.CreateStringFragments(p.model) AS f;
create clustered index index_idprod_substrmodel on [Products_dictionary]([model],[Id_product])
フルテキストインデックスは、テーブル全体を毎回スキャンするのではなく、文字列値から作成されたディクショナリに基づいているため、複雑で集中的な文字列検索を実行する必要がある場合、通常は「移動」オプションです(これは、 '%一部のテキスト% 'タイプの検索)、ディクショナリを参照するクエリオプティマイザーが判断すると、代わりにインデックスシークを使用します。
テーブルを作成し、100,000レコードを入力しました。
次のような通常の検索パターンを使用するだけです:
_select * from products where model like '%' + 'kind' +'%'
_
同じクエリを使用して、1行_select * from products where model like '%' + 'human' +'%'
_を返す他のWordと同じクエリを使用して、1102レコードが返されたことに注意してください。
現在、フルテキストインデックスを使用しています。
クエリの使用:select * from products where freetext(model,'kind')
スキャンの代わりにインデックスシークを使用するため、返される行が少ない場合は、実際に最適に機能します。
_select * from products where freetext(model,'Human')
_
注意する別のこと:
なんらかのブロッキングが発生している場合、クエリが予想よりも「遅くなる」ことがあります。これは、誰かがレコードを更新または挿入しようとして、その行を含むページで(X)ロックを保持しているときに発生する可能性があります。全テーブルスキャンのため、 '%some text%'形式を使用してクエリを検索するときに要求するのと同じページです。
フルテキストインデックスを使用すると、その時点で更新されたまったく同じレコードを検索しない限り、インデックスシークを使用してこれらの状況を回避できます。
多くの検索バリエーション、ファジーマッチング、「スペルチェック」バリエーションもあり、これらは非常に役立ちます。
適切な検索クエリの作成に役立つ本SQL Server 2008のプロフルテキスト検索があります。