2009-04-29 8 views
18

残念ながら、いくつかのvarcharフィールドでLIKE '%ABC%'を使用する必要がある非常に大きなテーブルから実行されるクエリがあるため、ユーザーが部分名などを検索できるようになります。SQL Server 2005SQL Serverインデックス - LIKEクエリの改善?

これらのvarcharフィールドのインデックスは、LIKEを使用しているときに選択クエリのパフォーマンスを向上させるか、基本的にインデックスを無視してそのような場合にフルスキャンを実行しますか?

LIKEを使用するとパフォーマンスを向上させる方法は他にありますか?

答えて

18

これらの列にフルテキスト検索を追加し、SQL Serverのフルテキストクエリ機能を使用する場合のみ。

それ以外の場合は、インデックスは役に立ちません。

+0

ありがとう、それは私が残念なことに思ったものです。スピードアップに役立ついくつかのLIKE節を削除しました。 – schooner

2

「%ABC%」のように、常にフルテーブルスキャンを実行します。その周りに方法はありません。

あなたには2つの方法があります。まずフルテキスト検索ですが、このような問題のために設計されていますので、まずそれを見てみましょう。

また、データを非正規化してターゲットフィールドを適切なトークンに前処理し、これらの可能な検索語句を別々の1対多の検索テーブルに追加することが適切な場合もあります。たとえば、私のデータが常にパターン 'AAA/BBB/CCC'を含むフィールドで構成されていて、ユーザーがBBBで検索していた場合、挿入/更新時にそれをトークン化して削除時に削除します。これは、アプリケーションコードではなくトリガーを使用すると、と多くの場合、が優先されるケースの1つになります。

これは実際には最適な手法ではなく、データがアプローチに適している場合にのみ使用し、何らかの理由で全文検索(および本当に受け入れられないスキャン)。それはさらにメンテナンスの頭痛を引き起こす可能性があります。

8

フルテキストインデックス以外の唯一の方法は、「LIKE ABC%」を使用することです。検索語の両端にワイルドカードを追加しないでください。 。

あなたの要件は、あなたが運の外出、検索語の両端にワイルドカードを持っている必要がありますようなものであるならば...

マルク・

+0

これは 'LIKE ABC%'に対してのみ機能するのですか、 'LIKE%ABC'でも有効ですか?また、なぜ片方向ワイルドカードで動作するのか不思議です...そのことを詳しく教えてください。 –

+0

@TomPažourek:うーん、電話帳を想像してみてください。姓が**の人を探しているのなら、** Smiで始まると、すぐにスミスとスミザーズなどが見つかります。しかし、姓が**の場合**(名前のどこにでも)など、誰かを検索するとソートされた名前リスト(**インデックス**が本当に何であるか)を持っている 'chuk'は本当にあなたを大いに助けません - その文字列を持つものを見つけるために**すべての名前**をスクロールしなければなりません彼らの名前 –

+0

ありがとう。 :-)どのようなデータ構造が使用されているか知っていますか?それはトライの何らかの形ですか? '%ABC'はインデックスを使用しませんか?あなたは、両端のワイルドカードは動作しないと述べました。右端のワイルドカードは使えますか? –

-2

は、その列の統計を作成します。 sql srever 2005では、文字列検索を最適化しているので、それに賛成する可能性があります。あなたが潜在的にインデックス(複数可)を追加することによって、パフォーマンスの改善を見ることができます

11

、それが具体的に多くを依存:)

あなた断定列がどのくらいの行の合計サイズのですか?いくつの行が一致すると思われますか?述語と一致するすべての行、または上位1行または上位n行だけを返す必要がありますか?

選択性/一意性(返される行が少ない)で値を検索し、述語列が行サイズ全体の小さな部分である場合、索引は非常に便利です。それでもスキャンされますが、索引はソース表よりページあたりの行数が多くなります。あなたは、実際の実行計画を見れば

create table t1 (v1 varchar(100), b1 varbinary(8000)) 
go 
--add 10k rows of filler 
insert t1 values ('abc123def', cast(replicate('a', 8000) as varbinary(8000))) 
go 10000 
--add 1 row to find 
insert t1 values ('abc456def', cast(replicate('a', 8000) as varbinary(8000))) 
go 

set statistics io on 
go 
select * from t1 where v1 like '%456%' 
--shows 10001 logical reads 

--create index that only contains the column(s) to search across 
create index t1i1 on t1(v1) 
go 
select * from t1 where v1 like '%456%' 
--or can force to 
--shows 37 logical reads 

あなたはエンジンがインデックスをスキャンしていた見ることができます:ここでは

は合計行サイズは全体の検索するには、列のサイズよりもはるかに大きい場合の例であります一致する行のブックマークルックアップ。または、このプランを単独で使用することを決定していない場合は、オプティマイザにインデックスを直接使用するよう指示できます。select * from t1(index(t1i1))ここで、v1は '%456%'のようになります。

高度に選択的な数少ない列だけを検索する列がある場合は、複数の索引を作成し、縮小方法を使用できます。例えば。あなたの選択性の高いインデックスからIDのセット(またはあなたのPKが何であれ)を決定し、その小さなPKセットに対するフィルターを使って、あまり選択的でない列を検索します。

大量の行を返す必要がある場合は、ほとんどの場合、テーブルスキャンを使用する方がよいでしょう。

可能な最適化は、テーブル定義の詳細とデータの選択性に大きく依存します。

HTH! -アドリアン

+0

また、ワイド表の列のサブセットを戻す場合は、INCLUDE索引も考慮してください。 –

+0

また、照会しているデータも考慮してください。たとえば、NULLおよび/または空の文字列を除外したフィルタリングされたインデックスを追加し、クエリでそのインデックスを使用すると、パフォーマンスが大幅に向上します。索引は小さくなり、次にLIKEがスキャンするために少なくなります。 –

関連する問題