SQL LIKE句の使い方|部分一致・ワイルドカード・エスケープ

記事の目的

SQL の LIKE 句を使って、文字列を「ぴったり一致」ではなく「あいまい」に検索できるようになります。

ワイルドカード %_ の意味、前方一致・後方一致・中間一致の書き方、%_ そのものを検索したいときのエスケープ、否定形の NOT LIKE 、そして 3 製品で挙動が大きく違う「大文字・小文字の区別」までを、PostgreSQL・MySQL・SQL Server の製品差に注意しながら整理します。

記事のサンプルテーブルの前提

本記事では、説明用に次のような products テーブルがある前提で進めます。

  • id :自動採番(商品 ID)
  • product_name :商品名(文字列)
  • category :カテゴリ
  • price :価格

実際の CREATE TABLE 文は別記事で扱います。本文中では次のサンプルデータがすでに入っているものとして説明します。

サンプルは6行です。100%林檎ジュース(id=5)と 100円チョコ(id=6)はどちらも「100」で始まるため、後段の「エスケープ」セクションで % を文字として扱う必要性を実例で確認します。

目次

最短の答え

products から「商品名に “ジュース” を含む商品」を検索します。

  • 主要な RDBMS(PostgreSQL / MySQL / SQL Server)で共通して使える基本形
SELECT id, product_name
FROM products
WHERE product_name LIKE '%ジュース%';

実行結果は次のようになります。

  • 「林檎」で始まる商品を検索(前方一致)
SELECT id, product_name FROM products
WHERE product_name LIKE '林檎%';
  • 「ジュース」で終わる商品を検索(後方一致)
SELECT id, product_name FROM products
WHERE product_name LIKE '%ジュース';

ここから先は、%_ の意味、エスケープ、製品差を順に解説します。

LIKE 構文の基本

LIKEWHERE 句の中で、列の値を「パターン」と比較する演算子です。

SELECT 列 FROM テーブル
WHERE 文字列の列 LIKE 'パターン';
  • LIKE の右側には文字列パターンを書きます。
  • パターンの中で、%_ の 2 つの記号が特別な意味を持ちます(ワイルドカード)。
  • パターンが列の値と一致した行だけが結果に含まれます。
  • 元のテーブルは変わりません。あくまで検索条件です。

ワイルドカードを使わずに WHERE product_name LIKE '緑茶' と書いた場合は、= 演算子と同じく「完全一致」になります。LIKE の真価はワイルドカードを使うときに現れます。

ワイルドカード ── [ % ] と [ _ ]

主要な RDBMS(PostgreSQL / MySQL / SQL Server)で共通して覚えるべきワイルドカードは、まず %_ の 2 つです。

記号意味
%0文字以上の任意の文字列(何でも・空でも OK)
_ちょうど1文字の任意の文字(必ず1文字。0文字や2文字は不可)

%_ の違いは、初心者が最初につまずくところです。% は「あってもなくてもいい」、_ は「必ず1文字必要」と覚えます。

なお、SQL Server には [abc] (候補のうちいずれか1文字)や [^d] (指定文字以外)といった角括弧によるパターン指定もあります。3製品で共通ではないため、本記事では %_ を中心に扱い、SQL Server 固有の角括弧パターンは「エスケープ」のセクションで補足します。

-- 例1: % は 0 文字以上の任意の文字列
SELECT product_name FROM products WHERE product_name LIKE '林檎%';
-- 例2: _ は必ず 1 文字
SELECT product_name FROM products WHERE product_name LIKE '_ジュース';

前方一致・後方一致・中間一致

% をどこに置くかで、検索の意味が変わります。これは LIKE で最もよく使う使い分けです。

パターン意味用例
'林檎%'前方一致:「林檎」で始まる商品名が「林檎」で始まるもの
'%ジュース'後方一致:「ジュース」で終わる商品名が「ジュース」で終わるもの
'%林檎%'中間一致:「林檎」を含む商品名のどこかに「林檎」を含むもの
-- 前方一致:「林檎」で始まる
SELECT id, product_name FROM products
WHERE product_name LIKE '林檎%';
-- 中間一致:「林檎」を含む
SELECT id, product_name FROM products
WHERE product_name LIKE '%林檎%';

「だいたいこれを含むものを取りたい」というユーザー検索の意図には、中間一致( '%…%' )が最もよく合います。ただし、後述する「インデックスの罠」があるため、件数が大きいテーブルでは使いすぎに注意します。

エスケープ ── [ % ] や [ _ ] そのものを検索したいとき

商品名に %_ を含む場合、そのまま書くとワイルドカードと解釈されてしまいます。

-- これは「100 で始まるすべての商品名」と解釈される
SELECT product_name FROM products WHERE product_name LIKE '100%';

実行すると、100%林檎ジュース(id=5)だけでなく、100円チョコ(id=6)も両方ヒットします。

「商品名がパーセント記号 % を含むものだけ」を取りたい場合は、エスケープ文字を使って「これはワイルドカードではない」と明示します。

-- 「100%」(文字としての % を含む)で始まる商品名(3 製品共通の書き方)
SELECT id, product_name FROM products
WHERE product_name LIKE '100!%%' ESCAPE '!';
  • !% :エスケープ文字 !% を打ち消し、「文字としての %」の意味になります。
  • 末尾の % :エスケープされていないので「以降に何があってもよい」というワイルドカードのまま。
  • ESCAPE '!' :エスケープ文字を ! に指定しています。

100円チョコ% を含まないので除外されました。ESCAPE の文字は ! でなくてもよく、パターンに登場しない文字を1つ選んで指定します(よく使われるのは !# などです)。

エスケープ文字のよくある選び方と製品差

3製品とも ESCAPE 'X' 構文に対応しており、上の書き方が共通で使えます。ただし、エスケープ文字を省略した場合の挙動は製品で異なります。

製品エスケープ文字を省略したとき
PostgreSQL既定で \ がエスケープ文字(ただし設定により変わるため ESCAPE 明示が安全)
MySQL既定で \ がエスケープ文字。文字列リテラル側でも \ がエスケープに使われるため、SQL モードや書き方で挙動が変わる
SQL Serverエスケープ文字に既定はなく、% をリテラルにしたい場合は ESCAPE を明示するか、後述の角括弧 [%] を使う

3製品で同じ動きをさせたい場合は、ESCAPE '!' のように使わない記号を毎回明示するのが最も安全です。

SQL Server 固有:角括弧でリテラルを表す

SQL Server では [ ] を使う独自のパターン指定があり、これを % のエスケープ目的で使うこともできます。

-- SQL Server のみ:[%] は「文字としての %」の意味
SELECT id, product_name FROM products
WHERE product_name LIKE '100[%]%';

PostgreSQL や MySQL では [%] はそのまま3文字の文字列として扱われ、意図通りに動きません。3製品共通で書きたい場合は、上記の ESCAPE '!' 方式を使います。

NOT LIKE ── 含まないものを検索

「LIKE の逆」は NOT LIKE で書きます。

-- 「ジュース」を含まない商品
SELECT id, product_name FROM products
WHERE product_name NOT LIKE '%ジュース%';

NOT LIKELIKE と同じワイルドカード( % _ )が使えます。ただし NULL の扱いには注意が必要です(後述)。

大文字・小文字の扱い(製品差)

LIKE の挙動でいちばん製品差が大きいのが、大文字・小文字を区別するかです。「同じ SQL でも結果が違う」典型例なので、扱う製品ごとに必ず確認します。

製品既定の挙動区別したい/したくないとき
PostgreSQL区別する(大文字小文字は別の文字として扱う)区別しない検索は ILIKE を使う
MySQL既定は区別しない(照合順序 utf8mb4_0900_ai_ci 等が既定の場合)区別したいなら BINARY 演算子や _bin 照合順序を使う
SQL Server照合順序による。日本語環境の既定は区別しないことが多い区別したいなら COLLATE 〜_BIN2 を指定する

つまり、同じ WHERE product_name LIKE 'apple%' を書いても、PostgreSQL では Apple がヒットしないのに、MySQL では Appleapple もヒットする、ということが普通に起きます。

PostgreSQL で大文字小文字を無視したい場合は ILIKE という独自演算子があります。詳しい使い分けや pg_trgm を使った高速化は別記事「PostgreSQL ILIKE と大文字小文字を無視したあいまい検索」で扱います。

-- PostgreSQL の例:ILIKE は大文字小文字を区別しない
SELECT product_name FROM products
WHERE product_name ILIKE 'apple%';

上記の既定挙動は版や照合順序の設定で変わります。本番環境では各製品の現行ドキュメントとサーバ設定で確認してください。

落とし穴・注意点

LIKE は手軽ですが、実務では混乱しがちな点があります。

NULL の扱い

LIKENULL を扱うと、結果は UNKNOWN になります。WHERE 句の中では UNKNOWN は条件不成立として扱われるため、NULL の行は結果から外れます。

-- product_name が NULL の行は、LIKE でも NOT LIKE でもヒットしない
SELECT product_name FROM products WHERE product_name LIKE '%林檎%';     -- NULL は対象外
SELECT product_name FROM products WHERE product_name NOT LIKE '%林檎%'; -- NULL は対象外

NULL を結果に含めたい場合は、WHERE product_name IS NULL OR product_name NOT LIKE '%…%' のように明示します。

中間一致・後方一致はインデックスが効きにくい

LIKE のインデックスの効きは、パターンの形と製品の実装で変わります。一般的には次のような傾向があります。

  • 前方一致( '林檎%' ):通常の B-tree インデックスが使える可能性があります。
  • 中間一致( '%林檎%' )・後方一致( '%林檎' ):通常の B-tree インデックスでは効きにくく、フルテーブルスキャンになりやすい検索です。

実際の挙動は、列の照合順序、データ型、関数の有無、統計情報、各 DBMS の実行計画によって変わります。件数が大きいテーブルで '%…%' を多用したクエリが遅いと感じたら、EXPLAIN (PostgreSQL / MySQL)や実行プラン表示(SQL Server)で確認します。

中間一致を高速化したい場合の方策は次のようなものがあります。

  • 可能なら前方一致だけで済むようにデータの持ち方を変える
  • PostgreSQL なら pg_trgm 拡張 + GIN インデックスで中間一致を高速化
  • MySQL なら全文検索インデックス( FULLTEXT )を併用
  • そもそも全文検索なら別エンジン(Elasticsearch 等)を検討

詳しい高速化の話はパフォーマンス系の別記事で扱います。

マルチバイト文字・改行・前後の空白

日本語などのマルチバイト文字は、LIKE のパターンでも問題なく使えます。ただしサーバ・クライアントの文字コード設定がズレていると、検索がヒットしない不可解な挙動になります。

列の値に前後の空白や改行が混じっていると、ぱっと見一致しそうな文字列でもヒットしません。怪しいときは TRIM などで一度確認します。

複雑なパターンは正規表現を使う

%_ の 2 種類だけでは表現できないパターン(「数字 3 桁+ハイフン+数字 4 桁」など)は、LIKE ではなく正規表現の関数・演算子を使います。

  • PostgreSQL: 演算子、SIMILAR TO
  • MySQL:REGEXPRLIKE
  • SQL Server:限定的( PATINDEX / CLR 関数等)

製品差が大きいので、移植性が必要なときは LIKE で表現できる範囲に収めるのが安全です。

まとめ

LIKE は文字列のあいまい検索の基本ですが、%_ の意味・前後中央の使い分け・% 自体の検索のためのエスケープ・NULL の扱い・3 製品で違う大文字小文字の挙動、の 5 点を押さえれば、現場のほとんどのケースに対応できます。PostgreSQL の ILIKE や高速化の話は別記事で深掘りします。

目次