web-dev-qa-db-ja.com

複数の列でMySQLのSELECT .. LIKEクエリを高速化する方法は?

MySQLテーブルがあり、非常に頻繁にSELECT x, y, z FROM table WHERE x LIKE '%text%' OR y LIKE '%text%' OR z LIKE '%text%'クエリ。どんな種類のインデックスも物事をスピードアップするのに役立ちますか?

テーブルには数百万のレコードがあります。検索を高速化するものがある場合、データベースファイルによるディスク使用量とINSERTおよびDELETEステートメントの速度に深刻な影響がありますか? (UPDATEは実行されません)

更新:投稿後すぐに、クエリでLIKEが使用される方法に関する多くの情報と議論を見ました。私は解決策を指摘したいと思います しなければならない 使用する LIKE '%text%'(つまり、探しているテキストの先頭に%ワイルドカードが追加されます)。また、セキュリティを含む多くの理由で、データベースはローカルでなければなりません。

42
Tom

テキスト列のインデックスは、左からN文字のインデックスを作成することで機能するため、インデックスを作成してもクエリの速度は上がりません。 LIKE '%text%'を実行すると、テキストの前に可変数の文字が存在する可能性があるため、インデックスを使用できません。

すべきことは、そのようなクエリをまったく使用しないことです。代わりに、MyISAMテーブルでMySQLがサポートするFTS(全文検索)のようなものを使用する必要があります。また、MyISAM以外のテーブル用にこのようなインデックスシステムを自分で作成するのは非常に簡単です。実際のテーブルに単語とその関連IDを保存する別のインデックステーブルが必要です。

62
reko_t

インデックスは、先頭のワイルドカードとのテキストマッチングには役立ちません。インデックスは次の用途に使用できます。

LIKE 'text%'

しかし、私はそれがそれをカットしないと推測しています。このタイプのクエリでは、検索できるレコードの量をスケーリングする場合、実際に全文検索プロバイダーを確認する必要があります。私の優先プロバイダーは Sphinx 、非常にフル機能/高速などです。 Lucene も一見の価値があります。 MyISAMテーブルのフルテキストインデックスも機能しますが、最終的に大量の書き込みがあるデータベースのMyISAMを追求することはお勧めできません。

21
Michael

インデックスはnotを使用して、検索条件がワイルドカードで始まるクエリを高速化できます。

LIKE '%text%'

インデックスは、次の形式の検索語に使用できます(選択性に応じて使用される場合があります)。

LIKE 'text%'

13
Mitch Wheat

場合によっては、表示しているフィールドが頻繁に空であるか、定数が含まれている場合、like/rlikeとともにインデックスを使用してクエリを高速化できることを追加します。

その場合、固定値の「and」句を追加することにより、インデックスを使用してアクセスされる行を制限できるようです。

私は通常、多くのタグを含まない巨大なテーブルで「タグ」を検索するためにこれを試しました。

SELECT * FROM objects WHERE tags RLIKE("((^|,)tag(,|$))" AND tags!=''

タグにインデックスがある場合、検索されている行を制限するために使用されることがわかります。

10
OderWat

Mysql5.1をmysql5.7にアップグレードしてみてください。

約70,000件のレコードがあります。そして、次のSQLを実行します。

select * from comics where name like '%test%'; 

Mysql5.1では2000msかかります。そして、mysql5.7またはmysql5.6では200msが必要です。

6
lingceng

別の方法:

これらの文字列をREVERSEdにして計算列を保持し、使用することができます

SELECT x, y, z FROM table WHERE x LIKE 'text%' OR y LIKE 'text%' OR z LIKE 'text%' OR xRev LIKE 'txet%' OR yRev LIKE 'txet%' OR zRev LIKE 'txet%' 

保存された永続列を追加する方法の例

ALTER TABLE table ADD COLUMN xRev VARCHAR(N) GENERATED ALWAYS AS REVERSE(x) stored;

xRevyRevなどにインデックスを作成します。

全表スキャンを回避する別の代替方法は、サブストリングを選択してhavingステートメントでチェックすることです。

SELECT 
    al3.article_number,
    SUBSTR(al3.article_number, 2, 3) AS art_nr_substr,
    SUBSTR(al3.article_number, 1, 3) AS art_nr_substr2,
    al1.*
FROM
    t1 al1 
    INNER JOIN t2 al2 ON al2.t1_id = al1.id
    INNER JOIN t3 al3 ON al3.id = al2.t3_id
WHERE
    al1.created_at > '2018-05-29'
HAVING 
    (art_nr_substr = "FLA" OR art_nr_substr = 'VKV' OR art_nr_subst2 = 'PBR');
0
Juri Sinitson