web-dev-qa-db-ja.com

LIKE '%...%'ワイルドカードクエリのPL / SQLパフォーマンスチューニング

Oracle11gデータベースを使用しています。
ご存知かもしれませんが、文字列の前に「%」を付けてワイルドカードクエリを使用すると、列インデックスは使用されていませんおよび全表scanが発生しています。

この種のクエリを改善する方法について明確な提案はないようですが、次のクエリを最適化する方法に関する経験から、いくつかの貴重な情報を共有できます。

SELECT * 
  FROM myTable 
 WHERE UPPER(CustomerName) like '%ABC%' 
    OR UPPER(IndemnifierOneName) like '%ABC%' 
    OR UPPER(IndemnifierTwoName) like '%ABC%';

...ここで、3つの列はすべてタイプvarchar2(100)であり、[〜#〜] abc [〜#〜]は変数入力パラメーターの値です。

@すべての提案[〜#〜] contex [〜#〜]インデックス、私のデータは毎日いつでも更新され、このインデックスに注意してください再同期が必要なため、not150万行のテーブルに適したオプションです。申し訳ありません。

P.S.私はすべての答えに賛成しますので、それらを続けてください。

15
Грозный

すでに述べたように、名前の列にctxコンテキストインデックスを追加できます。

少数のレコードが更新されると仮定すると、1つのオプションはインデックスを毎日更新することです。 (そしてそれがいつ起こったかを記録する)

次に、最終更新日列とインデックスを検索対象のテーブルに追加します。

Ctxインデックスをスキャンして、変更されていない古いデータの大部分を探し、従来のLIKEを使用して、更新されたデータのわずかな割合から選択することができるはずです。

WHERE (lastupdated<lastrefresh AND contains(name,'%ABC%')) 
   OR (lastupdated>lastrefresh AND name like '%ABC%')

注:クエリプランが少し精神的になる場合があります(行IDへのビットマップ変換がたくさんあります)。その場合、ORの2つの部分をUNIONALLクエリに分割します。例:

SELECT id FROM mytable   
    WHERE 
    (lastupdate>lastrefresh and name LIKE '%ABC%')
    UNION ALL
    SELECT id FROM mytable   
    WHERE lastupdate<lastrefresh and CONTAINS(name, '%ABC%', 1) > 0
7
Kevin Burton

唯一の最適化は、そのタイプのクエリを使用せず、代わりにデータベースプラットフォームのネイティブ機能を使用することです。

Oracleテキストを参照してください: http://www.Oracle.com/technetwork/database/enterprise-edition/index-098492.html

SQL Serverに関連する質問に対する一般的な答えは、全文検索です。

6
Fosco

UPPER()は何よりもまずインデックスを強制終了します。正規表現の使用を検討してください。初期 %は通常のインデックススキャンを回避する場合がありますが、常にフルテーブルスキャンになるとは限りませんが、FTSよりも高速なフルインデックススキャンになります。

'ABC'は可変です。そうでない場合は、関数インデックスが最適です。

3
Samuel

この種のクエリが避けられない場合があります。URLからドメインを抽出するか、プレフィックスとサフィックスが付いたWordからルートを抽出します。

カスタムトークナイザーの有無にかかわらず、フルテキストインデックスに頼ることができます。

または、検索する文字列の数が有限であり、事前にわかっている場合(たとえば、URLから抽出する必要のある限られたドメイン名のセットを使用している場合)、インデックスを作成できる決定論的関数を使用できます。

http://www.akadia.com/services/ora_function_based_index_2.html

0
Tim