sql likeで速く正確に探す方法: 構文から最適化まで

本記事ではSQLのLIKE句によるあいまい検索を基礎から解説します。%と_のワイルドカード、前方・後方・部分・完全一致、NOT LIKE、ESCAPEでのリテラル化、照合/大文字小文字の違いまで、実行例で実務に直結する使い方が学べます。

目次

SQLのLIKE句とは何か

sql+like+wildcard

SQLのLIKE句は、文字列に対してパターンマッチングを行うための述語で、いわゆる「あいまい検索」を簡潔な記法で実現します。等価比較(=)では拾えない「含む・始まる・終わる」といった条件を、ワイルドカードを用いたパターンで表現できるのが特徴です。sql like は正規表現ほど複雑ではありませんが、日常的な検索要件の多くをシンプルに満たせるため、アプリケーションからの検索、レポート作成、データクレンジングなど幅広い用途で使われます。

あいまい検索を実現する仕組みと用途

LIKE句は、対象文字列と「パターン」を比較し、パターン内のワイルドカードが任意の文字に対応する仕組みで一致・不一致を判定します。これにより、完全一致でなくても条件に合致するデータを取り出せます。基本的な使いどころは次の通りです。

  • ユーザー名や商品名などの前方一致・後方一致・部分一致検索
  • メールアドレスのドメイン抽出やファイル拡張子の判別など、末尾や特定部分でのフィルタリング
  • ログやメモ欄など非構造テキストのキーワード抽出
  • データ品質チェック(例:想定外の接頭辞・接尾辞を含む値の洗い出し)

LIKEによる判定は文字列の比較ルール(照合順序)に影響を受けるため、大文字小文字やアクセントの扱いはデータベースや設定に依存します。これは結果の一致・不一致に直結する重要な前提です。

ワイルドカード(% と _)の役割

LIKE句で使用するワイルドカードは主に2種類です。役割を理解して適切に組み合わせることで、実用的なパターンを表現できます。

  • %(パーセント): 0文字以上の任意の連続した文字列に一致します。先頭・末尾・中間のいずれにも置けるため、前方一致・後方一致・部分一致を表現できます。
  • _(アンダースコア): 任意の1文字に一致します。桁数が決まっているコードや固定長の一部をあいまいに指定したいときに有効です。

これらは「特殊文字」であり、パターン中に現れると通常の文字ではなくワイルドカードとして解釈されます。したがって、% や _ 自体を文字として扱いたい場合には、別途の手段が必要になります。なお、改行やスペースも文字として扱われ、_ は1文字(多バイト文字環境では1コードポイント相当)に対応しますが、厳密な扱いは製品や設定によって異なることがあります。

文字クラスや範囲指定の可否(製品ごとの違い)

標準SQLのLIKEは本質的に「% と _ の2種類のみ」を前提としており、正規表現のような文字クラス(例:[abc])や範囲指定(例:[a-z])は規格上は定義されていません。ただし、一部のデータベースは拡張を提供しています。代表的な可否は次のとおりです。

  • Microsoft SQL Server: LIKEで角括弧による拡張パターンが利用可能です。例として、[aeiou](いずれかの1文字)、[a-z](範囲)、[^0-9](否定)などがサポートされます。
  • MySQL: LIKEは % と _ のみをサポートし、文字クラスや範囲指定は不可です(高度なパターンは REGEXP 演算子など別機能を利用)。
  • PostgreSQL: LIKEは % と _ のみで、文字クラスや範囲指定は不可です(正規表現演算子 ~ や SIMILAR TO が代替として用意されています)。
  • Oracle Database: LIKEは % と _ のみで、文字クラスや範囲指定は不可です。
  • Snowflake: LIKEは % と _ のみをサポートし、文字クラス・範囲指定は不可です。
  • SQLite: LIKEは % と _ のみですが、別演算子の GLOB では Unix風ワイルドカード([] など)を利用できます。
  • Microsoft Access(Jet/ACE): 既定のパターンでは [] による文字クラスや範囲指定が利用可能です(環境設定によりワイルドカード記号が変わる点に注意)。

このように、LIKEで文字クラスや範囲指定を直接使えるかはデータベースごとに差があります。要件が「単純なあいまい検索」か「細かなパターン制御」かを見極め、製品のサポート範囲に合わせてsql like、正規表現、あるいは別演算子を使い分けるのが重要です。

LIKE句の基本構文と書き方

sql+like+wildcard

SQL LIKE(sql like)は、文字列パターンに基づいて一致判定を行う基本演算子です。このセクションでは、LIKE句の正しい書き方、ワイルドカードを用いたパターンの作り方、%や_を文字として扱うためのエスケープ指定、そして評価結果におけるNULLの取り扱いまでを、実務で迷わないレベルで整理します。

基本構文とパターンの作り方

LIKE句は、左辺の文字列式が右辺のパターンに一致するかを評価します。NOTを付けると否定(非一致)になります。パターンにはワイルドカードを使います。

-- 構文
<string_expression> [NOT] LIKE <pattern> [ESCAPE <escape_char>]

-- ワイルドカードの基本
%   : 0文字以上の任意の文字列
_   : ちょうど1文字の任意の文字
  • パターンは文字列リテラルでもプレースホルダでも指定できます。
  • ワイルドカードは任意の文字(空白・記号・数字・文字)にマッチします。
  • 複数のワイルドカードを組み合わせて、柔軟なパターンを作れます。

例:単純なLIKEによる一致判定

-- 先頭が "SQL" で始まる
SELECT * FROM articles
WHERE title LIKE 'SQL%';

-- 末尾が ".com" で終わる
SELECT * FROM users
WHERE email LIKE '%.com';

-- 3文字目が必ず存在し、"AB" + 任意1文字 + "-123" に一致
SELECT * FROM products
WHERE sku LIKE 'AB_-123';

-- プレースホルダを用いる(バインド値にパターンを渡す)
SELECT * FROM customers
WHERE name LIKE :pattern;   -- :pattern に 'A%' や '%son%' を設定

アプリケーションから利用する際は、文字列結合でパターンを作るよりも、バインド変数にあらかじめワイルドカード込みの文字列(例: ‘%keyword%’)を渡す方が安全です。

エスケープ指定で%や_を文字として扱う

検索キーワードに「%」や「_」そのものが含まれる場合、それらをワイルドカードではなく文字として扱う必要があります。これにはESCAPE句を使います。

ESCAPE句の使い方と注意点

-- 例1: 「100%」を含むテキストを検索(% を文字として扱う)
SELECT * FROM notes
WHERE body LIKE '%100\%%' ESCAPE '\';

-- 例2: 「_admin」を含むテキストを検索(_ を文字として扱う)
SELECT * FROM roles
WHERE name LIKE '%\_admin%' ESCAPE '\';

-- 例3: エスケープ文字自体(\)を検索
SELECT * FROM files
WHERE path LIKE '%\\tmp%' ESCAPE '\';
  • ESCAPEで指定できるのは1文字のみです(複数文字は不可)。
  • エスケープ文字は、% と _ を直前に置いたときだけ特別な意味を持ちます。その他の文字の直前では通常はそのままの扱いです。
  • パターンの末尾がエスケープ文字で終わるのは不正(または不定)になり得ます。ユーザー入力を用いる場合は末尾の孤立したエスケープを事前に除去・補完してください。
  • エスケープ文字自身を検索するには二重に書きます(例: ESCAPE ‘\’ のときは ‘\\’)。

バックスラッシュ等の既定エスケープの違い

データベース製品によって、ESCAPEを明示しない場合の「バックスラッシュ(\)」の扱いが異なります。移植性を高めるには、必要に応じて常にESCAPEを明示するのが安全です。

  • PostgreSQL: 既定でバックスラッシュがエスケープとして機能します。’\%’ や ‘\_’ でワイルドカードをリテラル化できます。必要なら ESCAPE で別の文字に変更可能です。
  • MySQL: 多くの設定でバックスラッシュがLIKEパターンのエスケープとして機能します(SQLモードにより挙動が変わる場合があります)。ESCAPE句で任意のエスケープ文字を指定可能です。
  • SQL Server / Oracle: 既定ではバックスラッシュは特別扱いされません。% や _ を文字として扱う場合は ESCAPE で明示するか、SQL Serverでは ‘[%]’ や ‘[_]’ のような角括弧表記を使えます。
  • SQLiteなど: バージョンやビルド設定によってESCAPEやバックスラッシュの扱いが異なることがあります。利用環境のドキュメントを確認してください。
-- SQL Server の角括弧例(ESCAPEなし)
SELECT * FROM texts WHERE val LIKE '%[%]off%';  -- 「%off」を含む
SELECT * FROM texts WHERE val LIKE '%[_]admin%'; -- 「_admin」を含む

評価結果とNULLの扱い(TRUE/FALSE/UNKNOWN)

LIKEは三値論理(TRUE / FALSE / UNKNOWN)で評価されます。WHERE句ではTRUEだけが採用され、FALSEとUNKNOWNは除外されます。NULLの存在がUNKNOWNを生む点に注意が必要です。

  • 左辺がNULLの場合: NULL LIKE ‘A%’ の結果は UNKNOWN です(行は返りません)。
  • パターンがNULLの場合: ‘ABC’ LIKE NULL も UNKNOWN です(常に不採用)。
  • NOT LIKE でも、比較対象がNULLなら結果は TRUE ではなく UNKNOWN になります。
-- 1) NULL の行は返らない
SELECT * FROM t
WHERE col LIKE 'A%';   -- col が NULL の行は UNKNOWN → 除外

-- 2) NULL も含めたい場合の明示
SELECT * FROM t
WHERE col LIKE 'A%' OR col IS NULL;

-- 3) NOT LIKE と NULL の安全な書き方
SELECT * FROM t
WHERE col IS NOT NULL
  AND col NOT LIKE 'A%';

-- 4) パターンがNULLになり得るとき
--    :pat が NULL だと LIKE の結果は UNKNOWN → 全件0件になる
--    デフォルト動作として「全件」を許すなら COALESCE で '%' を補う
SELECT * FROM t
WHERE col LIKE COALESCE(:pat, '%');  -- :pat が NULL なら全件一致(要件に応じて使用)

まとめると、sql like を用いる際は、NULLが絡むとUNKNOWNになることを前提に、IS NULL/IS NOT NULL との併用や、バインド値のデフォルト指定(COALESCEなど)を設計に織り込むのが実務的なベストプラクティスです。

代表的なパターンの使い分け

sql+like+wildcard

前方一致(先頭が一致)

前方一致は「特定の接頭辞で始まる」データを取り出すときに使います。ワイルドカード % を末尾に置くのが基本で、オートコンプリートやコード体系(例:PRD-XXXXX)の先頭一致抽出に適しています。sql like を使うと、固定の頭文字だけを指定して柔軟に検索できます。

-- 例:製品コードが「PRD-」で始まるレコードを取得
SELECT product_code, product_name
FROM products
WHERE product_code LIKE 'PRD-%';

-- 例:年度1桁をアンダースコアで固定し、その後方を前方一致
SELECT report_id, title
FROM reports
WHERE report_id LIKE 'R_2025%';  -- _ は任意1文字、% は0文字以上
  • 使いどころ:コードの接頭辞、部署記号、タグの先頭一致など
  • ポイント:パターンは『固定文字列%』の形にする

後方一致(末尾が一致)

後方一致は「特定の接尾辞で終わる」データを対象にします。ファイル拡張子や末尾の区分コードなど、終端が意味を持つ場面で便利です。% を先頭に置くのが定型です。

-- 例:CSVファイルのみ抽出
SELECT file_name, size_bytes
FROM file_catalog
WHERE file_name LIKE '%.csv';

-- 例:支店コードが「-JP」で終わる行を取得
SELECT branch_code, branch_name
FROM branches
WHERE branch_code LIKE '%-JP';
  • 使いどころ:拡張子、リージョン識別子、終端フラグの判別
  • ポイント:パターンは『%固定文字列』の形にする

部分一致(中間に含む)

部分一致は「文字列のどこかに指定語が含まれる」レコードを探します。全文検索ほど重厚ではなく、軽い含有チェックに適した方法です。% を前後に置くのが定番です。

-- 例:説明文に「返金」を含む商品を検索
SELECT product_id, description
FROM products
WHERE description LIKE '%返金%';

-- 例:タイトルに「2025」をどこかに含む
SELECT doc_id, title
FROM documents
WHERE title LIKE '%2025%';
  • 使いどころ:キーワード含有、注意喚起語の検知、簡易ハイライト対象の抽出
  • ポイント:パターンは『%キーワード%』の形にする

完全一致(ワイルドカード未使用)

完全一致は、ワイルドカードを使わずに LIKE を用いる書き方です。実質的には「ちょうど同じ文字列」を意味し、コーディングルール上「比較演算を sql like に統一したい」といったケースで選ばれることがあります。

-- 例:部署名が正確に「総務部」と一致
SELECT dept_id, dept_name
FROM departments
WHERE dept_name LIKE '総務部';
  • 使いどころ:明示的にパターンマッチ(ワイルドカードなし)を表現したいとき
  • ポイント:% や _ を含めない文字列を指定する

否定検索(NOT LIKE)

NOT LIKE は、特定のパターンに一致しないデータを除外・抽出するときに用います。「仮コードを除きたい」「下書き状態を除外したい」といった品質担保に役立ちます。

-- 例:一時コード(TEMP_で始まる)を除外
SELECT code, label
FROM master_codes
WHERE code NOT LIKE 'TEMP_%';

-- 例:メモ欄に「WIP」を含まない行のみ取得
SELECT task_id, note
FROM tasks
WHERE note NOT LIKE '%WIP%';
  • 使いどころ:一時データ・ドラフト・除外対象のフィルタリング
  • ポイント:LIKE と同じパターン記法をそのまま否定にできる

複数条件の組み合わせ(AND/OR)

sql like の条件は AND/OR で自由に組み合わせられます。複数の前方一致を OR で束ねたり、前方一致と後方一致を AND で同時に満たす、といった複合条件が典型です。評価順を明確にするために括弧を使うのが安全です。

-- 例1:二つの接頭辞のいずれかに一致(OR)
SELECT ticket_id, summary
FROM tickets
WHERE summary LIKE 'BUG:%'
   OR summary LIKE 'SEC:%';

-- 例2:接頭辞と接尾辞の両方を満たす(AND)
SELECT order_no, note
FROM orders
WHERE order_no LIKE 'WEB-%'
  AND note     LIKE '%-OK';
  • OR:候補の拡張(いずれか一致)に有効
  • AND:より厳密な絞り込み(すべて一致)に有効
  • 括弧で条件をグルーピングし、意図した論理になるように明示する

データベースごとの挙動の違い

sql+like+wildcard

同じsql likeでも、データベース製品ごとにパターン解釈・照合順序・エスケープの既定が大きく異なります。移植や複数エンジン併用の現場では、「期待どおりに一致しない」「インジェクション対策でエスケープしたのに誤判定する」などの齟齬が起きがちです。ここでは主要エンジンの差分ポイントを整理し、現場で迷いやすい大文字小文字・アクセントの感度、ESCAPE句の違いも具体例で解説します。

SQL Serverの拡張パターン([], [^], – の範囲指定)

SQL ServerのLIKEは、標準の%(任意長)、_(任意1文字)に加え、角括弧による拡張パターンをサポートします。これにより正規表現なしでも簡易な文字クラス・範囲・否定が可能です。

  • 文字クラス: [abc] は a または b または c
  • 範囲指定: [a-z]、[0-9]
  • 否定: [^0-9] は「数字以外」
-- A〜Cで始まるコード
WHERE code LIKE '[A-C]%';

-- 数字を含まない値
WHERE val LIKE '%[^0-9]%';

-- 角括弧そのものを検索する(リテラル化)
WHERE txt LIKE '%[[]note[]]%'  -- [ と ] は [[] と []] で表現

一致の大小・アクセント感度は照合順序(COLLATE)に従います。既定の多くは「CI」(Case Insensitive=大文字小文字を区別しない)ですが、列・式単位でCOLLATEを切り替えられます。

-- 大文字小文字を区別して前方一致
WHERE name COLLATE Latin1_General_CS_AS LIKE 'Abc%';

Snowflakeの照合順序と大文字小文字・アクセントの扱い

SnowflakeはLIKE/ILIKEの双方を提供し、さらに照合(COLLATE)を列・式・テーブル定義で付与できます。一般に、

  • LIKE: 大文字小文字を区別(コレーションやパラメータに依存)
  • ILIKE: 大文字小文字を区別しない(ケース非感度のパターン一致)

コレーションを式に適用して挙動を明示できます(例は概念)。

-- 大文字小文字やアクセント非感度の比較例(コレーション名は環境に合わせる)
WHERE city COLLATE 'en-ci-ai' LIKE 'cafe%';

-- ILIKEでケースを無視
WHERE title ILIKE '%report%';

アクセントの扱い(è と e を同一視するか)は、指定したコレーションのオプションに依存します。移植時は、LIKEかILIKEか、加えてCOLLATE適用の有無を統一してください。

MySQL/PostgreSQLのLIKEとILIKEの違い

MySQLとPostgreSQLでは、sql likeのケース感度の決まりが異なります。特に「ILIKEの有無」と「照合順序の効き方」が重要です。

要点:

  • MySQL: ILIKEは存在しません。LIKEの大小・アクセント感度は列・比較のコレーションに依存します(例: utf8mb4_0900_ai_ci はアクセント非感度・ケース非感度)。
  • PostgreSQL: ILIKEをサポート(ケース非感度)。LIKEは基本的にケース感度です。アクセント同一視はデフォルトでは行われません。
-- MySQL: コレーションでケース非感度に
WHERE name LIKE 'abc%' COLLATE utf8mb4_0900_ai_ci;

-- MySQL: 明示的に「バイナリ(完全区別)」に寄せる
WHERE name LIKE 'Abc%' COLLATE utf8mb4_bin;

-- PostgreSQL: ケースを無視
WHERE name ILIKE 'abc%';

-- PostgreSQL: 明示的にLIKE(ケース感度)
WHERE name LIKE 'Abc%';

大文字小文字・アクセント感度の設定ポイント

  • SQL Server: COLLATEで CS/CI(大文字小文字)と AS/AI(アクセント)を制御。列既定に依存するため、式単位でCOLLATEを明示すると移植性が上がります。
  • Snowflake: COLLATEを列や式に付与してケース・アクセントのポリシーを指定。ILIKEはケース非感度のパターン一致として利用可能。
  • MySQL: コレーション名で ai/ci/cs/as を選択(例: utf8mb4_0900_ai_ci)。LIKEの結果はコレーション次第。列定義・比較時のCOLLATE指定・接続の既定を混在させないこと。
  • PostgreSQL: ケース非感度はILIKEを使用。アクセント無視は拡張(unaccent)などの前処理で対処するのが一般的です。

ESCAPEの指定方法と既定の挙動

  • SQL Server: ESCAPE句をサポート。既定のエスケープ文字はありません。特殊記号はESCAPEで指定するか、[%]や[_]、[[] のように角括弧でリテラル化可能。
-- % と _ を文字として扱う
WHERE val LIKE '%\_%' ESCAPE '\';  -- SQL ServerではESCAPEを必ず明示
  • Snowflake: ESCAPE句をサポート。省略時はバックスラッシュ(\)がエスケープとして機能します。
WHERE val LIKE '\_%' ESCAPE '\';  -- _ をリテラル化
  • MySQL: LIKEのエスケープは原則「\」(NO_BACKSLASH_ESCAPESモード無効時)。SQL標準のESCAPE句は使用できません(バージョン既定ではサポート対象外)。
-- MySQL: % と _ をリテラルとして検索
WHERE path LIKE '%\_%';  -- SQLモードにより挙動が変わる点に注意
  • PostgreSQL: ESCAPE句をサポート。既定のエスケープは「\」。必要に応じて変更可能。
WHERE txt LIKE 'A!_%' ESCAPE '!';  -- ! をエスケープ文字に指定

Access/SQLiteでのLIKE利用時の注意

AccessとSQLiteは、他エンジンと比べて「ワイルドカードの文字体系」と「ケース感度・エスケープ」の前提が異なります。

  • Microsoft Access:
    • 既定(ANSI-89)ではワイルドカードが %/_ ではなく、*(任意長)と ?(任意1文字)、#(数字1桁)を使用します。角括弧 […] と範囲 [-]、否定 [!…] は利用可能です。
    • ANSI-92モードを有効にすると、% と _ に切り替わります。ODBC接続やAccessプロジェクトではANSI-92が既定の場合があります。
    • 移植時は「クエリがどのモードで解釈されるか」を必ず確認し、同一データでも結果が変わらないか検証してください。
  • SQLite:
    • LIKEは % と _ のみを解釈し、SQL Serverのような [a-z] 等の文字クラスは非対応(GLOB演算子では [ … ] が使えますがLIKEとは別機能)。
    • 既定ではASCII範囲で大小を区別しないことがあります。厳密な比較が必要なら PRAGMA case_sensitive_like = ON を検討してください。
    • ESCAPE句は使用できますが、既定のエスケープ文字はありません(\ は特別扱いではない)。必要時は必ず ESCAPE を明示します。
-- Access(ANSI-89既定)例
WHERE Title LIKE 'Report*'      -- 任意長
AND   Code  LIKE 'A#??'         -- A + 数字 + 任意2文字

-- SQLite: LIKEで [a-z] は使えない点に注意
WHERE name LIKE 'A%'            -- OK
-- WHERE name LIKE '[A-Z]%'     -- NG(LIKEでは不可)

-- SQLite: エスケープを明示
WHERE path LIKE '%!_%' ESCAPE '!';

このように、sql likeの「同じ書き方」が各DBで同じ意味になるとは限りません。要件に応じて、コレーションとエスケープ、利用可能なパターン機能を明示的にコントロールすることが、移植性と検証容易性の鍵です。

パフォーマンス最適化の勘所

sql+like+optimization

LIKE句は手軽さの反面、パターンの書き方とインデックス設計次第で性能が大きく変わります。ここでは、sql like(SQL LIKE)の高速化に直結する要点を、現場でのチューニング手順に沿って整理します。

インデックス利用と先頭ワイルドカードの影響

多くのRDBMSのB-treeインデックスは「先頭からの連続したリテラル」による範囲探索が最も効率的です。言い換えると、LIKEの先頭にワイルドカード(% や _)が来るかどうかが成否を分けます。

  • インデックスが効く典型:前方一致(’abc%’)は「abc〜abdの手前まで」の範囲探索にマップでき、Index Seekが期待できます。
  • 効きにくい典型:先頭ワイルドカード(’%abc’ や ‘_abc%’)は先頭キーが不定のため、全表/全インデックス走査になりやすいです。
  • 両端ワイルドカード(’%abc%’)は部分一致のため、原則フルスキャン寄りになります。
-- 前方一致はB-treeで範囲探索が可能
CREATE INDEX idx_users_name ON users(name);
SELECT id, name
FROM users
WHERE name LIKE '田中%';  -- Index Seek(範囲)になりやすい

-- 先頭ワイルドカードは全走査になりやすい
SELECT id
FROM users
WHERE name LIKE '%中%';   -- Scan + Filter になりやすい

後方一致を高速化したい場合は、補助列の導入が現実解です。たとえば「末尾が ‘@gmail.com’」の検索では、元文字列を反転した補助列(name_rev)を用意し、前方一致に変換します。これによりB-treeで範囲探索可能になります(補助列のメンテナンスと書き込み増はトレードオフ)。

-- 反転コピー済みの補助列(name_rev)にインデックスを付与
CREATE INDEX idx_users_name_rev ON users(name_rev);

-- '@gmail.com' で終わる(後方一致)を、前方一致に置換して検索
SELECT id
FROM users
WHERE name_rev LIKE 'moc.liamg@%';  -- 'gmail.com' の反転
  • カバリングの徹底:SELECT句で必要な列をインデックスに含める(含む/包含インデックス)と、Key Lookupを回避できI/Oを削減できます。
  • 複合インデックスの順序:等価条件(tenant_id など)→ 前方一致対象の列(name など)の順で作ると選択度が上がり、LIKEの範囲探索がより効きます。
  • 選択度の意識:’a%’ のような低選択度パターンはヒット件数が多く、インデックスのメリットが薄れます。先に絞り込み可能な条件を活用し、母集団を減らしてからLIKEで当てるのが定石です。

照合順序や関数適用が検索性能に与える効果

照合順序(collation)や関数適用は、LIKEのインデックス利用に直接影響します。見落とすと「正しい結果だが遅い」状態を招きます。

  • 照合順序とインデックス整合性:インデックスが作成された照合順序と、クエリ時の照合順序が一致しているとシークしやすくなります。クエリ側で列に別の照合順序を明示したり、暗黙に変換が入るとインデックスが使われにくくなります。
  • 列に関数をかけない:UPPER/LOWER/TRIMなどを列側に適用すると、列が式(関数適用後)となりシーク不可になりがちです。対策は次の通りです。
    • 保存時に正規化(例:すべて小文字)しておく。
    • 計算列・関数インデックス(機能があるDB)を用い、関数適用後の値にインデックスを張る。
    • 関数は可能な限り「パラメータ側」に寄せる(例:検索語をあらかじめ正規化)。
  • 型と長さの一致:LIKEは文字列比較です。列とパラメータの型/照合順序/文字セットが揃っていないと暗黙変換が起こり、インデックスが利かなくなる場合があります。プレースホルダやバインド変数の型も列側に合わせておきましょう。
-- 悪い例:列に関数を適用(インデックスが効きにくい)
WHERE UPPER(name) LIKE 'TANAKA%'

-- 良い例:列はそのまま、検索語をアプリ側で正規化
WHERE name LIKE 'tanaka%'

実行計画でボトルネックを見極める

sql like の高速化は「なんとなくの勘」ではなく、実行計画で裏付けることが重要です。以下の観点でボトルネックを特定します。

  • アクセスパスの確認:Index Seek(範囲)になっているか、Scan+Filterになっていないか。LIKEのパターンを変えた時にアクセスパスがどう変わるかを比較します。
  • 残差述語(Residual Predicate):インデックスで絞った後に別の演算でさらにフィルタしている場合、CPU比率が高くなります。関数適用や照合順序の不整合が残差化の原因になっていないか確認します。
  • Key Lookupの有無:シーク後に行データを取りに行くルックアップが多発するとI/Oが増えます。必要列をインデックスに含めて解消できないか検討します。
  • 見積り行数と実行行数の乖離:LIKEは選択度推定が難しく、統計情報が古いと見積り誤差が拡大します。統計情報を更新し、乖離が解消するか確認します。
  • パラメータ依存性:パターンのばらつき(’x%’は高選択度、’%x%’は低選択度)が大きい場合、キャッシュされた計画が次回以降に不適合になることがあります。可変なパターンでは、再最適化の仕組みやステートメント分割(前方一致とそれ以外で分ける)を検討します。
-- 望ましい計画のイメージ
-- WHERE name LIKE '田中%'        -> Index Seek(範囲)
-- SELECT 句はインデックス包含   -> Key Lookup 回避(カバリング)

-- 避けたい計画のイメージ
-- WHERE name LIKE '%田中%'       -> Index/Heap Scan + Filter(高I/O・高CPU)
-- WHERE LOWER(name) LIKE '...'   -> 関数適用でシーク不可、残差述語が増える

最後に、改善は「測定→変更→再測定」のサイクルで行いましょう。LIKEのパターン、インデックスの列順序/包含、照合順序の整合、統計情報の鮮度という4点を揃えると、実行計画は自然と最短経路に近づきます。

実践サンプル集

sql+wildcard+query

ここでは、現場でそのまま使えるSQL LIKE(sql like)の実践例をまとめます。まずは共通のサンプルテーブルを定義し、その上で前方一致・後方一致・部分一致・除外・エスケープ・SQL Server固有の文字範囲まで、頻出パターンをコンパクトに確認します。

サンプルテーブルの前提

以下のusersテーブルを前提にクエリ例を示します。

CREATE TABLE users (
  user_id   INT PRIMARY KEY,
  name      VARCHAR(100),
  email     VARCHAR(255),
  note      VARCHAR(255)
);

INSERT INTO users (user_id, name,      email,                              note)
VALUES
  (1, '佐藤 花子',    'hanako.sato@example.com',        NULL),
  (2, '斎藤 太郎',    'taro.saito@company.co.jp',       NULL),
  (3, 'John Smith',  'john.smith@example.co.uk',        NULL),
  (4, '鈴木_一郎',    'ichiro.suzuki@test-example.com', NULL),
  (5, '中村%真',      'makoto.nakamura@sample.net',     NULL),
  (6, '山田(退職)', 'yamada+sales@example.com',       '退職済'),
  (7, 'suzuki 次郎',  'jiro.suzuki@example.com',        'VIP顧客');
user_id name email note
1 佐藤 花子 hanako.sato@example.com
2 斎藤 太郎 taro.saito@company.co.jp
3 John Smith john.smith@example.co.uk
4 鈴木_一郎 ichiro.suzuki@test-example.com
5 中村%真 makoto.nakamura@sample.net
6 山田(退職) yamada+sales@example.com 退職済
7 suzuki 次郎 jiro.suzuki@example.com VIP顧客

よく使うクエリ例

以下の各パターンは、運用での絞り込み・監査・ダッシュボードの検索条件などで頻出です。パターン文字列は必要に応じてバインド変数に置き換えてください。

名前の前方一致で検索する

先頭が特定の文字列で始まる行を抽出します。ユーザー入力の「最初の数文字検索」に使います。

-- 「佐藤」で始まる名前
SELECT user_id, name
FROM users
WHERE name LIKE '佐藤%';

-- バインド変数(例: :prefix = '斎藤')
SELECT user_id, name
FROM users
WHERE name LIKE (:prefix || '%');  -- 連結はDBに合わせて || or CONCAT を選択

メールドメインを後方一致で抽出する

末尾が特定のドメインで終わるメールを抽出します。組織ドメインのフィルタや監査で利用します。

-- 企業ドメインが example.com のメールアドレス
SELECT user_id, email
FROM users
WHERE email LIKE '%@example.com';

-- 国別ドメイン(.co.jp)を含むアドレス(単純化した後方一致)
SELECT user_id, email
FROM users
WHERE email LIKE '%.co.jp';

特定語を含む部分一致

文字列の中間に指定語が含まれる行を抽出します。メモや役職、タグ列の検索に有効です。

-- 名前に「太郎」を含む
SELECT user_id, name
FROM users
WHERE name LIKE '%太郎%';

-- メモに「VIP」を含む
SELECT user_id, note
FROM users
WHERE note LIKE '%VIP%';

NOT LIKEで特定パターンを除外する

特定のパターンに一致する行を除外します。集計やレポートでノイズデータを排除する際に使えます。

-- example.com ドメイン以外のメール
SELECT user_id, email
FROM users
WHERE email NOT LIKE '%@example.com';

-- 名前に「(退職)」を含まない現役ユーザー(全角括弧に注意)
SELECT user_id, name
FROM users
WHERE name NOT LIKE '%(退職)%';

%や_を文字として検索する(ESCAPE使用)

%と_はLIKEのワイルドカードです。その文字自体を検索したい場合はESCAPE句でエスケープ文字を指定します。

-- アンダースコア「_」を含む名前(例: 鈴木_一郎)
SELECT user_id, name
FROM users
WHERE name LIKE '%\_%' ESCAPE '\';

-- パーセント「%」を含む名前(例: 中村%真)
SELECT user_id, name
FROM users
WHERE name LIKE '%\%%' ESCAPE '\';

文字範囲を使った検索(SQL Serverのみ)

SQL ServerではLIKEの拡張として、角括弧[]で文字クラスや範囲を指定できます([A-C], [^0-9] など)。

-- ドメイン名(@の直後)が a~c で始まるメール(SQL Server)
SELECT user_id, email
FROM users
WHERE email LIKE '%@[a-c]%';

-- 名前がASCIIの英字で始まらないユーザー(否定クラス ^)
SELECT user_id, name
FROM users
WHERE name LIKE '[^A-Za-z]%';

上記の角括弧やハット(^)、ハイフン(-)による文字クラス・範囲はSQL Serverの拡張構文です。他のDBでは動作しないため、利用環境がSQL Serverであることを確認してから使用してください。

よくある落とし穴と回避策

sql+like+optimization

sql like(SQLのLIKE句)は手軽に使える一方で、文字種や照合順序、関数の適用方法によって「思った通りに一致しない」「インデックスが効かず遅い」といった問題が起こりがちです。ここでは、現場で頻出する落とし穴と、それを避けるための具体的な対策を整理します。

全角・半角やUnicode正規化の差異

日本語環境では、全角/半角や合成文字(濁点・結合文字)の違いでLIKEの一致結果が変わることがあります。たとえば「ガ」と「ガ」、「A」と「A」、「ば(結合文字)」と「ば(合成済み)」などは、バイト列やコードポイントが異なるため、単純なLIKEでは一致しません。

  • 落とし穴: ‘_’ は「1文字」を表しますが、結合文字やサロゲートペアを含むと期待通り1文字と数えないことがあります。絵文字や結合記号が混ざるデータで、’__’ などのパターンがズレるケースが典型例です。
  • 落とし穴: 文字幅・アクセント・大文字小文字の感度は照合順序に依存します。環境差(開発/本番やクラウドサービス間)で挙動が変わることがあります。
  • 回避策: アプリ/ETL側でUnicode正規化(多くはNFKCまたはNFC)を施し、正規化済み列を別途保持してそこに対してLIKEを行う。元データは監査用に残し、検索用は規格化してインデックスを張る二重保管戦略が安全です。
  • 回避策: DBの照合順序(コレーション)を要件に合わせて設定。幅/カナ/アクセント/大文字小文字の感度オプション(例: SQL ServerのCI/CS, AI/AS, KS/WS、PostgreSQLやMySQLのICU/UCAコレーション)を明示します。
  • 回避策: ‘_’ を多用せず、結合文字が混ざる可能性があるデータでは基本的に ‘%’ を使う。単一文字マッチが必要なら、対象データを正規化した上でテストを十分に行います。
-- 例: 正規化済み列を用意(生成列 + インデックス)
-- 製品ごとに記法は異なるが、発想は「正規化してからLIKE」
-- 正規化関数が無い場合はETL/アプリ側でNFKC化して格納

正規表現・全文検索との使い分け

sql likeは「部分一致」や「前方一致」には適していますが、複雑なパターンや長文の検索には不向きです。用途別に正規表現や全文検索を使い分けると、精度と性能を両立できます。

  • 落とし穴: 内部コードやフォーマット検証(例: A-1234形式)のような厳密なパターンをLIKEで表現すると誤検知や複雑化でメンテ不能になりがち。
  • 落とし穴: 長文カラムに対して ‘%キーワード%’ を多発すると、インデックスが効かず全表走査となり、遅延やロック時間増大を招きます。
  • 回避策(正規表現): フォーマット厳格な検索は REGEXP_LIKE(Oracle/BigQuery)、REGEXP(MySQL)、~ / ~*(PostgreSQL)などを活用。製品によっては正規表現インデックスやトライグラム(pg_trgm)で高速化可能です。
  • 回避策(全文検索): 自然文や複数語、スコアリングが必要な場合は全文検索へ。例: SQL ServerのCONTAINS/CONTAINSTABLE、MySQLのMATCH … AGAINST、PostgreSQLのto_tsvector/to_tsquery。ステミングやストップワードで精度と速度が上がります。
  • 使い分け目安:
    • LIKE: 前方一致や単純な部分一致(先頭ワイルドカードを避けられる場合)
    • REGEXP: パターン検証や文字クラス・繰り返しなどが必要
    • 全文検索: 長文・複数語・ランキング・言語処理が必要

代替関数(INSTR/POSITION/REGEXP/ILIKE)の検討

要件次第では、LIKEより意図が明確で移植性や性能に優れる書き方があります。特に位置取得や大文字小文字の扱いは関数で表現した方が保守性が高い場合があります。

  • INSTR/LOCATE(Oracle/MySQL など): 部分文字列の開始位置を返すため、「含むかどうか」を > 0 で判定できます。位置を使ったロジック(切り出し等)と一貫して書けます。機能インデックスや生成列により高速化可能。
  • POSITION(sub IN str)(標準SQL): INSTRに相当。製品間の移植性を重視するならこちらの記法を検討。
  • REGEXP/REGEXP_LIKE: 構造化パターンに強力。過剰適用はコスト増になるため、必要最小限+適切な索引(例: pg_trgm、ベンダー提供の正規表現アクセラレーション)を併用。
  • ILIKE(PostgreSQL): 大文字小文字を区別しないLIKE。注意: 通常のB-Treeでは索引が効きにくく、性能要件がある場合はcitext型、lower()の関数インデックス、pg_trgmのGIN/GiSTなどを検討。
  • 回避策(移植性/性能):
    • ケース無視は「関数で変換」より「コレーション/データ型」で解決できないか検討(例: case-insensitiveな照合順序、citext)。
    • 関数を使う場合は機能インデックス(関数インデックス、生成列+インデックス)をセットで設計。
    • 製品固有機能に寄せる場合は代替案(標準SQLのPOSITIONなど)も併記し、将来の移行コストを下げる。
-- 例: PostgreSQL で大文字小文字を無視した検索を高速化
-- 1) 関数インデックス
CREATE INDEX idx_users_name_lc ON users (lower(name) text_pattern_ops);
-- 2) 検索
SELECT * FROM users WHERE lower(name) LIKE 'suzuki%';
-- 3) 代替: ILIKE + pg_trgm 拡張でGINインデックス

TRIMやUPPER/LOWER適用によるスキャン増大

可視上の「余計な空白」や「表記ゆれ」を吸収するために、TRIMやUPPER/LOWERを列側に適用すると、多くのデータベースでインデックスが使えなくなり、全表スキャンの原因となります。

  • 落とし穴: WHERE LOWER(col) LIKE 'abc%' のように列に関数をかけると、SARGability(索引効用性)が失われることが多い。
  • 落とし穴: TRIM(col) も同様で、前後空白を吸収したいだけでも全スキャンになる場合がある。CHAR型の後続空白の扱いは製品と照合順序に依存。
  • 回避策:
    • 関数インデックス/生成列+インデックスを用意し、検索はその式に合わせる。
    • ケース無視は照合順序やデータ型(例: case-insensitiveコレーション、citext)で対応し、関数適用を避ける。
    • 前処理でデータを正規化(トリム・ケース正規化)して格納。アプリ/ETLでの一貫した入力バリデーションを徹底。
    • 前方一致は可能なら LIKE 'prefix%' に限定し、先頭ワイルドカード('%term%')を多用しない。必要なら専用索引(例えばトライグラム)を使う。
-- 例: MySQL 8.0 以降(生成列+インデックス)
ALTER TABLE customers
  ADD name_norm VARCHAR(255) GENERATED ALWAYS AS (LOWER(TRIM(name))) STORED,
  ADD INDEX ix_name_norm (name_norm);
SELECT * FROM customers WHERE name_norm LIKE 'suzuki%';

ポイントは、「列に関数をかける書き方」を避け、コレーション設定・正規化済み列・関数インデックスなどの仕組みで、sql like の可読性と性能を両立させることです。

まとめ(LIKE句を安全かつ効率的に使うために)

sql+query+optimization

sql like は手軽で強力なあいまい検索手段ですが、設計と運用を誤ると誤検出・性能劣化・セキュリティリスクを招きます。ここでは、安全かつ効率的に使いこなすための要点を最小限のチェックリストとして整理します。

  • パターン設計の原則
    • 必要最小限のワイルドカードに留める(例:前方一致で十分なら % を末尾のみにする)。
    • 先頭ワイルドカード(%foo)は極力避ける。インデックスが効かなくなりやすい。
    • 完全一致できる箇所は固定文字列にする(SARGableに保つ)。
  • セキュリティと入力対策
    • 必ずプレースホルダ/パラメータ化クエリを使用し、ユーザー入力を直結しない。
    • %_ を文字として扱う必要がある場合は ESCAPE を明示し、入力時に適切にエスケープする。
    • ワイルドカードの大量付与などリソース濫用を防ぐため、入力長やパターンの複雑さを制限し、タイムアウトを設定する。
  • 正確性を担保する照合と文字の扱い
    • 大文字小文字・アクセント感度は照合順序や実装差に依存するため、要件に合わせて明示設定する。
    • 全角/半角、Unicode正規化(NFC/NFD)の差異で不一致が起きないよう、保存時または検索前に正規化ポリシーを決める。
    • NULLLIKE では一致判定ができず UNKNOWN になるため、必要に応じて COALESCE 等で対処する。
  • 性能最適化の勘所
    • 前方一致(foo%)は一般にインデックスが効きやすいが、列に関数適用(LOWER(col) 等)するとスキャンになりやすい。
    • 照合順序変更やアクセント無視の設定は検索負荷に影響しうるため、実行計画を確認する。
    • 頻出検索キーは派生列や専用インデックスの検討(例:正規化済み・前処理済み列)。
  • 代替手段の選択
    • 語彙検索・複雑パターン・高精度や高性能が必要なら、正規表現、全文検索、またはケース非感知の比較演算子など適材適所で使い分ける。
    • 製品ごとの差異(例:ケース感度、ESCAPE の既定)を事前に把握し、環境間移植時はリグレッションテストを実施する。
  1. 入力を正規化し、期待する一致条件(前方/後方/部分/否定)を明確化する。
  2. インデックスが効くパターンを優先し、先頭ワイルドカードや列関数適用を避ける。
  3. エスケープ方針と照合順序を明示し、テストデータで境界ケース(大小・アクセント・全半角・NULL)を検証する。
  4. 実行計画とスロークエリを監視し、必要に応じて代替手段へ切り替える。

上記を守れば、sql like を安全かつ効率的に活用できます。まずは小さく検証し、実データでの挙動と性能を観察しながら運用にフィットさせていきましょう。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です