SQLのLIKEとは、%(0文字以上の任意の文字列)と _(任意の1文字)という2つのワイルドカードで、文字列のパターン一致を調べる演算子です。 name LIKE '%ジュース%' と書けば「ジュース」を含む行、name LIKE 'apple%' と書けば「apple」で始まる行が取り出せます。

ただし、使い込むと次のような疑問が出てきます。「50% という文字そのものを探したいときは?」「同じSQLなのにMySQLとPostgreSQLで件数が違う」「% から始まる検索は遅い?」。この記事では、PostgreSQL 16.15・MySQL 8.0.46・SQLite 3.45.1 で同じSQLを実行した結果を並べて、基本の書き方からエスケープ、大文字小文字、アプリからの安全な渡し方、インデックスまでを一通り確認します。上位の解説記事にあまりない「DB別の既定エスケープ文字」「照合順序による件数の違い」「EXPLAINでの確認」を、実際の出力つきで載せています。

MySQL 8.0 は2026年4月にサポートが終了しています。本記事の検証は手元の 8.0.46 で行いましたが、ここで扱うLIKE・ESCAPE・照合順序・EXPLAINの書き方は MySQL 8.4/9.7 でも同じ手順で使えます(違いがある箇所は本文で明記します)。

SQL LIKEの早見表

やりたいこと 書き方 一致する例 一致しない例
前方一致(〜で始まる) LIKE 'apple%' apple pie Pineapple
後方一致(〜で終わる) LIKE '%ジュース' グレープジュース ジュースセット
部分一致(〜を含む) LIKE '%ジュース%' ジュースセット ジューサー
任意の1文字 LIKE 'user_id' user_id、userXid user__id
含まない NOT LIKE '%ジュース%' apple pie グレープジュース
複数キーワードのどれか LIKE '%a%' OR LIKE '%b%' a か b を含む どちらも含まない
複数キーワードすべて LIKE '%a%' AND LIKE '%b%' a と b を含む 片方だけ
% そのものを探す LIKE '50!%%' ESCAPE '!' 50%オフ 500円引き
_ そのものを探す LIKE 'user!_id' ESCAPE '!' user_id userXid
大文字小文字を無視(PostgreSQL) ILIKE '%juice%' Apple Juice —
大文字小文字を区別(MySQL) LIKE '%Juice%' COLLATE utf8mb4_bin Apple Juice apple juice

DB別の違い(要点)

項目 PostgreSQL 16 MySQL 8.0 SQLite 3.45 SQL Server Oracle
既定のエスケープ文字 \ \ なし なし なし
ESCAPE句 対応 対応 対応 対応 対応
[ ] で文字集合 なし なし なし [%]、[a-c] なし
大文字小文字(既定) 区別する 照合順序しだい(既定は区別しない) ASCIIのみ区別しない 照合順序しだい 区別する
区別しない検索 ILIKE 既定の _ci 照合順序 既定の LIKE _CI 照合順序 UPPER() 同士で比較など
前方一致でB-tree索引 C照合 or *_pattern_ops 効く 条件つき 効く 効く

(SQL Server・Oracle の列は公式ドキュメントで確認した内容で、本記事の環境では実行していません。)

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「SQL LIKE部分一致とエスケープ|%・_の扱いをDB別にコピペ解説」

サンプルデータを用意する

以下の表で全DB共通のSQLを動かします。そのまま貼り付けて試せます。

DROP TABLE IF EXISTS items;
CREATE TABLE items (id INT PRIMARY KEY, name VARCHAR(50));
INSERT INTO items VALUES
 (1,'Apple Juice'),(2,'apple pie'),(3,'Pineapple'),(4,'グレープジュース'),
 (5,'ジュースセット'),(6,'50%オフ'),(7,'500円引き'),(8,'user_id'),(9,'userXid'),(10,'C:\temp');

MySQLだけは 'C:\temp' の \t がタブ文字に変換されて保存されます。 MySQLの文字列リテラルではバックスラッシュがエスケープ文字だからです。MySQLでは 'C:\\temp' と2つ重ねて書いてください。また、mysqlクライアントの文字コードが latin1 のままだと日本語の比較結果が変わるため、mysql --default-character-set=utf8mb4 で接続します。

前方一致・後方一致・部分一致を書く

基本の3パターン

SELECT id, name FROM items WHERE name LIKE 'apple%'     ORDER BY id;  -- 前方一致
SELECT id, name FROM items WHERE name LIKE '%ジュース'   ORDER BY id;  -- 後方一致
SELECT id, name FROM items WHERE name LIKE '%ジュース%'  ORDER BY id;  -- 部分一致

実行結果(3つのDBで比較)

SQL PostgreSQL 16 MySQL 8.0 SQLite 3.45
LIKE 'apple%' 2 apple pie 1 Apple Juice、2 apple pie 1 Apple Juice、2 apple pie
LIKE '%ジュース' 4 グレープジュース 4 グレープジュース 4 グレープジュース
LIKE '%ジュース%' 4、5 4、5 4、5

前方一致の件数がDBによって違います。 PostgreSQLはLIKEで大文字小文字を区別し、MySQL(既定の照合順序 utf8mb4_0900_ai_ci)とSQLiteは区別しないためです。詳しくは後半の「大文字小文字」の章で確認します。

_(アンダースコア)で1文字だけ任意にする

SELECT id, name FROM items WHERE name LIKE 'user_id' ORDER BY id;
 id |  name
----+---------
  8 | user_id
  9 | userXid
(2 rows)

_ は「ちょうど1文字」のワイルドカードです。 user_id という列名やコードをそのまま条件に書くと、userXid のような値まで一致します。_ を文字として探す方法は次の章で扱います。

NOT LIKE で除外する

SELECT id, name FROM items WHERE name NOT LIKE '%ジュース%' ORDER BY id;

結果は3つのDBとも id 1〜3、6〜10 の8件でした。NOT LIKE は NULL の行を返しません。 name が NULL の行は LIKE でも NOT LIKE でも結果が「不明」になるため、NULLも含めたいときは name NOT LIKE '%ジュース%' OR name IS NULL と書きます。

複数条件(OR/AND)

-- どちらかを含む
SELECT id, name FROM items WHERE name LIKE '%pie%' OR name LIKE '%ジュース' ORDER BY id;
-- すべてを含む(p を含み、かつ e で終わる)
SELECT id, name FROM items WHERE name LIKE '%p%' AND name LIKE '%e' ORDER BY id;
条件 結果(3DB共通)
'%pie%' OR '%ジュース' 2 apple pie、4 グレープジュース
'%p%' AND '%e' 1 Apple Juice、2 apple pie、3 Pineapple

キーワードが多いときの書き方もDBごとに用意されています。

DB 書き方
PostgreSQL name LIKE ANY (ARRAY['%pie%', '%ジュース'])(すべてなら LIKE ALL)
MySQL name REGEXP 'pie|ジュース$'(正規表現)
共通 OR を並べる。AND と混ぜるときは ( ... OR ... ) と括弧で囲む

WHERE category = 1 AND name LIKE '%a%' OR name LIKE '%b%' のように括弧を忘れると、AND が先に評価されて意図しない行まで返ります。OR を含む条件は括弧で囲むと覚えておくと安心です。

%や_を含む文字列を検索する(エスケープ)

エスケープしないとどうなるか

「50%」という文字を含む商品を探すつもりで LIKE '50%' と書くと、% がワイルドカードとして働きます。

SELECT id, name FROM items WHERE name LIKE '50%' ORDER BY id;
 id |   name
----+-----------
  6 | 50%オフ
  7 | 500円引き
(2 rows)

「50で始まる」という意味になり、500円引き も一致します。 % や _ を文字として扱うには、直前にエスケープ文字を置きます。

ESCAPE句を使う(全DB共通の書き方)

SELECT id, name FROM items WHERE name LIKE '50!%%'     ESCAPE '!' ORDER BY id;
SELECT id, name FROM items WHERE name LIKE 'user!_id'  ESCAPE '!' ORDER BY id;

実行結果(PostgreSQL 16・MySQL 8.0・SQLite 3.45 すべて同じ)

 id |  name
----+---------
  6 | 50%オフ
(1 row)

 id |  name
----+---------
  8 | user_id
(1 row)

'50!%%' は「!% =文字としての%」+「% =ワイルドカード」です。ESCAPE句で ! や # のような記号を指定する書き方が、どのDBでも同じ結果になるのでおすすめです。 バックスラッシュは文字列リテラル側でも特別な意味を持つDBがあり、書き方がDBごとに変わるためです。

既定のエスケープ文字(ESCAPE句なし)をDB別に比べる

ESCAPE句を書かずに \ でエスケープしたときの結果です。

SQL PostgreSQL 16 MySQL 8.0 SQLite 3.45
LIKE '50\%%' 6 50%オフ 6 50%オフ 0件
LIKE 'user\_id' 8 user_id 8 user_id 0件

PostgreSQLとMySQLは \ が既定のエスケープ文字、SQLiteには既定のエスケープ文字がありません。 SQLiteでは \ がただの文字として比較されるので、50\ で始まる行を探すことになり0件です。SQLiteでは必ず ESCAPE 句を書きます。

SQL Server と Oracle は公式ドキュメントで確認しました(本記事では未実行)。

DB 文字として%を探す書き方 公式ドキュメントの記載
SQL Server LIKE '5[%]'、LIKE '%30!%%' ESCAPE '!' ワイルドカードを [ ] で囲むと文字として扱える。ESCAPE句も使える
Oracle LIKE '%A\_B%' ESCAPE '\' ESCAPE を指定しない場合、既定のエスケープ文字はない

SQL Server では [ 自体も特別な文字([a-c] のような文字集合)になる点が他のDBと異なります。

バックスラッシュそのものを探す

C:\temp のようにバックスラッシュを含む値を探す場合です。

DB 書き方 結果
PostgreSQL 16 LIKE 'C:!\%' ESCAPE '!' 10 C:\temp
SQLite 3.45 LIKE 'C:!\%' ESCAPE '!' 10 C:\temp
MySQL 8.0 LIKE 'C:\\\\%' 10 C:\temp
MySQL 8.0 LIKE 'C:\\%' ESCAPE '!' 10 C:\temp
MySQL 8.0 LIKE 'C:\\%'(ESCAPEなし) 0件

MySQLは「文字列リテラル」と「LIKEのパターン」の2段階でバックスラッシュが解釈されます。 'C:\\%' はリテラルの段階で C:\% になり、パターンの段階で \% が「文字としての%」になるため0件です。ESCAPEを ! に変えると \ がただの文字になり、意図どおり一致します。

MySQLの NO_BACKSLASH_ESCAPES モード

MySQL の sql_mode に NO_BACKSLASH_ESCAPES を加えると、バックスラッシュは文字列リテラルでもLIKEでもエスケープ文字ではなくなります。

SET SESSION sql_mode = CONCAT(@@sql_mode, ',NO_BACKSLASH_ESCAPES');
SELECT id, name FROM items WHERE name LIKE '50\%%';             -- 0件
SELECT id, name FROM items WHERE name LIKE '50!%%' ESCAPE '!';  -- 6 50%オフ
SELECT id, name FROM items WHERE name LIKE 'C:\%';              -- 10 C:\temp

サーバー設定によって \ の意味が変わるため、ESCAPE句で記号を明示しておくと設定に左右されません。

大文字小文字・ひらがなカタカナの区別

サンプルに (11,'じゅーす'),(12,'juice'),(13,'ÄPFEL') を追加して比べました。

PostgreSQL:LIKE は区別、ILIKE は区別しない

SELECT id, name FROM items WHERE name LIKE  '%juice%';  -- 0件
SELECT id, name FROM items WHERE name ILIKE '%juice%';  -- 1 Apple Juice
SELECT id, name FROM items WHERE lower(name) LIKE lower('%JUICE%');  -- 1 Apple Juice

ILIKE はPostgreSQL独自の「大文字小文字を区別しないLIKE」です(演算子では ~~*)。全角の juice は半角とは別の文字なので、ILIKEでも一致しません。他のDBへ移す可能性があるSQLでは lower() 同士で比べる書き方が使いやすいです。

MySQL:照合順序(collation)で決まる

SQL(MySQL 8.0.46、列は utf8mb4_0900_ai_ci) 結果
LIKE '%juice%' 1 Apple Juice、12 juice
LIKE '%ジュース%' 4、5、11 じゅーす
LIKE 'apfel' 13 ÄPFEL
LIKE '%juice%' COLLATE utf8mb4_bin 0件
LIKE '%Juice%' COLLATE utf8mb4_0900_as_cs 1 Apple Juice
LIKE '%ジュース%' COLLATE utf8mb4_bin 4、5

MySQLの既定の照合順序 utf8mb4_0900_ai_ci は、大文字小文字・アクセント・全角半角・ひらがなカタカナを同じ文字として比べます。 あいまいに探したい検索窓には便利で、厳密に探したいときは COLLATE utf8mb4_bin を付けます。

検証中に気づいた点として、MySQL 8.0.46 では日本語用の utf8mb4_ja_0900_as_cs_ks(かな区別あり)を指定しても、LIKE では ジュース と じゅーす が一致しました。

SELECT 'ジュース' =    'じゅーす' COLLATE utf8mb4_ja_0900_as_cs_ks AS eq_ks,
       'ジュース' LIKE 'じゅーす' COLLATE utf8mb4_ja_0900_as_cs_ks AS like_ks,
       'ジュース' LIKE 'じゅーす' COLLATE utf8mb4_bin              AS like_bin;
+-------+---------+----------+
| eq_ks | like_ks | like_bin |
+-------+---------+----------+
|     0 |       1 |        0 |
+-------+---------+----------+

= ではかなを区別し、LIKE では区別しない結果でした。 ひらがなとカタカナを確実に分けてLIKE検索したいときは utf8mb4_bin を使うと意図どおりになります。照合順序そのものの比較は「MySQL照合順序(collation)の違い」の記事で詳しくまとめています。

SQLite:ASCIIの英字だけ区別しない

SELECT id, name FROM items WHERE name LIKE '%juice%';   -- 1 Apple Juice
SELECT id, name FROM items WHERE name LIKE 'äpfel';     -- 0件(Ä と ä は別扱い)
PRAGMA case_sensitive_like = ON;
SELECT id, name FROM items WHERE name LIKE '%juice%';   -- 0件
SELECT id, name FROM items WHERE name GLOB '*Juice*';   -- 1 Apple Juice

SQLiteのLIKEはASCIIの英字だけ大文字小文字を同一視します。 PRAGMA case_sensitive_like = ON で区別するように切り替えられ、GLOB(* と ? を使うパターン)は常に区別します。

大文字小文字の判断表

目的 PostgreSQL MySQL SQLite
区別せずに探す ILIKE または lower() 既定(_ci)のまま 既定のまま(英字のみ)
区別して探す 既定の LIKE COLLATE utf8mb4_bin GLOB または PRAGMA
全角半角・かなもまとめて探す 正規化した列を別に持つ utf8mb4_0900_ai_ci 正規化した列を別に持つ

アプリからLIKEを使う(プレースホルダとエスケープ関数)

検索窓の入力を条件にするときは、SQLを文字列連結せずプレースホルダ(? や %s)で渡し、そのうえで % _ をエスケープするのが基本です。プレースホルダはSQLインジェクションを防ぎますが、% や _ はLIKEのワイルドカードとしてそのまま働くため、「50%」と入力すると 500円引き まで一致します。

Python(sqlite3)

import sqlite3

def escape_like(s: str, esc: str = "!") -> str:
    """LIKE のワイルドカード(% と _)とエスケープ文字自体をエスケープする"""
    return (s.replace(esc, esc + esc)
             .replace("%", esc + "%")
             .replace("_", esc + "_"))

conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT)")
conn.executemany("INSERT INTO items(name) VALUES (?)",
                 [("50%オフ",), ("500円引き",), ("user_id",), ("userXid",), ("C:\\temp",)])

for keyword in ["50%", "user_id", "C:\\"]:
    raw = conn.execute("SELECT name FROM items WHERE name LIKE ? ORDER BY id",
                       (f"%{keyword}%",)).fetchall()
    safe = conn.execute("SELECT name FROM items WHERE name LIKE ? ESCAPE '!' ORDER BY id",
                        (f"%{escape_like(keyword)}%",)).fetchall()
    print(f"{keyword!r:12} エスケープなし={[r[0] for r in raw]}  エスケープあり={[r[0] for r in safe]}")

実行結果(Python 3.11)

'50%'        エスケープなし=['50%オフ', '500円引き']  エスケープあり=['50%オフ']
'user_id'    エスケープなし=['user_id', 'userXid']  エスケープあり=['user_id']
'C:\\'       エスケープなし=['C:\\temp']  エスケープあり=['C:\\temp']

エスケープ文字自体(ここでは !)を最初に置き換えるのがポイントです。順番を逆にすると、% を !% に変えたあとでその ! が !! になり、意味が変わってしまいます。

同じ escape_like() と ESCAPE '!' を、psycopg 3.3.6(PostgreSQL 16)と PyMySQL(MySQL 8.0)でも実行しました。

SQL = "SELECT name FROM items WHERE name LIKE %s ESCAPE '!' ORDER BY id"
cur.execute(SQL, (f"%{escape_like(keyword)}%",))
PostgreSQL '50%' → ['50%オフ']
PostgreSQL 'user_id' → ['user_id']
PostgreSQL 'C:\\' → ['C:\\temp']
MySQL '50%' → ['50%オフ']
MySQL 'user_id' → ['user_id']
MySQL 'C:\\' → ['C:\\temp']

ESCAPE文字を ! にそろえると、同じ関数がSQLite・PostgreSQL・MySQLのどれでもそのまま使えました。

JavaScript(Node.js 22 の node:sqlite)

import { DatabaseSync } from 'node:sqlite';

// LIKE のワイルドカード(% と _)とエスケープ文字自体をエスケープする
const escapeLike = (s, esc = '!') =>
  s.replaceAll(esc, esc + esc).replaceAll('%', esc + '%').replaceAll('_', esc + '_');

const db = new DatabaseSync(':memory:');
db.exec('CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT)');
const ins = db.prepare('INSERT INTO items(name) VALUES (?)');
for (const n of ['50%オフ', '500円引き', 'user_id', 'userXid', 'C:\\temp']) ins.run(n);

const stmt = db.prepare("SELECT name FROM items WHERE name LIKE ? ESCAPE '!' ORDER BY id");
for (const keyword of ['50%', 'user_id', 'C:\\']) {
  const rows = stmt.all(`%${escapeLike(keyword)}%`).map(r => r.name);
  console.log(keyword.padEnd(8), '→', rows);
}

実行結果(Node.js v22.22.2、node like_escape.mjs)

50%      → [ '50%オフ' ]
user_id  → [ 'user_id' ]
C:\      → [ 'C:\\temp' ]

Node.js 22 の node:sqlite は実行時に ExperimentalWarning を表示します。pg や mysql2 など他のドライバでも、escapeLike() とプレースホルダの組み合わせは同じです。SQL Server を使う場合は [ もエスケープ対象に加えると、[a-c] のような文字集合として解釈されるのを防げます。

インデックスが効くLIKE・効かないLIKE

判断表

パターン B-treeインデックス 理由
LIKE 'P00123%'(前方一致) 使える 先頭が決まるので範囲検索に変換できる
LIKE '%0123'(後方一致) 使えない 先頭が決まらず全件を調べる
LIKE '%0123%'(部分一致) 使えない 同上
LIKE 'P_0123%' 先頭の P までの範囲で使える _ の手前までが範囲になる

MySQL 8.0 の EXPLAIN

20万行の products(code) にインデックスを作って比べました。

CREATE INDEX idx_code ON products(code);
EXPLAIN ANALYZE SELECT * FROM products WHERE code LIKE 'P00123%';
EXPLAIN ANALYZE SELECT * FROM products WHERE code LIKE '%0123%';
-> Index range scan on products using idx_code over ('P00123' <= code <= 'P00123????...'),
   with index condition: (products.`code` like 'P00123%')  (cost=45.3 rows=100) (actual time=0.037..0.225 rows=100 loops=1)

-> Filter: (products.`code` like '%0123%')  (cost=20146 rows=22177) (actual time=0.122..68.4 rows=1140 loops=1)
    -> Table scan on products  (cost=20146 rows=199613) (actual time=0.0894..40.5 rows=200000 loops=1)

(1行目の ???? は長いため途中を省略しています。)前方一致は Index range scan、% 始まりは Table scan で20万行すべてを読んでいます。

PostgreSQL 16 の EXPLAIN(照合順序に注意)

CREATE INDEX idx_code ON products(code);
EXPLAIN SELECT * FROM products WHERE code LIKE 'P00123%';
 Seq Scan on products  (cost=0.00..4166.00 rows=20 width=34)
   Filter: ((code)::text ~~ 'P00123%'::text)

PostgreSQLでは、データベースの照合順序が C 以外(今回は C.UTF-8)だと、通常のB-treeインデックスはLIKEの前方一致に使われません。 varchar_pattern_ops(text型なら text_pattern_ops)を付けたインデックスを作ると使われます。

CREATE INDEX idx_code_pattern ON products(code varchar_pattern_ops);
EXPLAIN ANALYZE SELECT * FROM products WHERE code LIKE 'P00123%';
 Index Scan using idx_code_pattern on products  (cost=0.42..8.44 rows=20 width=34) (actual time=0.032..0.048 rows=100 loops=1)
   Index Cond: (((code)::text ~>=~ 'P00123'::text) AND ((code)::text ~<~ 'P00124'::text))
   Filter: ((code)::text ~~ 'P00123%'::text)
 Execution Time: 0.073 ms

'P00123%' が >= 'P00123' AND < 'P00124' の範囲検索に変換されているのがわかります。同じ表で LIKE '%0123%' は Seq Scan(Rows Removed by Filter: 198860、Execution Time: 39.563 ms)でした。

部分一致を速くする代替手段

手段 DB 向いている検索
pg_trgm + GINインデックス PostgreSQL LIKE '%..%'、ILIKE をそのまま速くしたい
FULLTEXT + ngramパーサ MySQL 日本語のキーワード検索(MATCH ... AGAINST)
検索用の列を別に持つ 共通 後方一致は文字列を反転した列+前方一致

pg_trgm で LIKE '%ジュース%' を実行した結果です(PostgreSQL 16、20万行中2,000件一致)。

CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE INDEX idx_name_trgm ON products USING gin (name gin_trgm_ops);
EXPLAIN ANALYZE SELECT count(*) FROM products WHERE name LIKE '%ジュース%';
   ->  Bitmap Heap Scan on products  (cost=21.59..95.27 rows=20 width=0) (actual time=0.808..2.141 rows=2000 loops=1)
         Recheck Cond: (name ~~ '%ジュース%'::text)
         ->  Bitmap Index Scan on idx_name_trgm  (cost=0.00..21.59 rows=20 width=0) (actual time=0.299..0.299 rows=2000 loops=1)
               Index Cond: (name ~~ '%ジュース%'::text)
 Execution Time: 2.275 ms

% 始まりのLIKEのまま、インデックスが使われました。 ILIKE '%ジュース%' でも同じGINインデックスが使われ、Execution Time は 2.551 ms でした。pg_trgm は3文字単位で索引を作るため、2文字以下の検索語では効果が出にくい点に注意します。MySQLでは ALTER TABLE products ADD FULLTEXT INDEX ft_name (name) WITH PARSER ngram; を作成し、MATCH(name) AGAINST('ジュース' IN BOOLEAN MODE) で LIKE と同じ2,000件が返ることを確認しました。全文検索の詳しい比較やインデックスが使われない他のパターンは、「PostgreSQL日本語全文検索」「SQLのインデックスが効かない10のパターン」の記事で扱っています。

よくあるつまずきと直し方

症状 原因 直し方
50% で探すと 500 も出る % がワイルドカード LIKE '50!%%' ESCAPE '!'
user_id で userXid も出る _ がワイルドカード LIKE 'user!_id' ESCAPE '!'
SQLiteで \% が効かない SQLiteは既定のエスケープ文字なし ESCAPE 句を書く
MySQLで C:\temp が見つからない INSERT時に \t がタブになった 'C:\\temp' で保存し直す
同じSQLでDBごとに件数が違う 大文字小文字・照合順序の差 ILIKE、COLLATE、lower() で明示
MySQLで日本語の一致がおかしい クライアントの文字コードが latin1 --default-character-set=utf8mb4
前方一致なのにSeq Scan(PostgreSQL) 照合順序が C 以外 text_pattern_ops のインデックス
NOT LIKE で NULL 行が消える NULL との比較は不明になる OR col IS NULL を足す

よくある質問

Q. LIKEと = はどちらが速いですか? A. ワイルドカードを含まない LIKE 'abc' は = 'abc' とほぼ同じ扱いになることが多いですが、意図が伝わりやすいので完全一致は = で書くのがおすすめです。末尾の空白の扱いなど細かな違いもあります。

Q. ESCAPE に使う文字は何がよいですか? A. 検索対象のデータにあまり出てこない1文字の記号(!、#、^ など)が扱いやすいです。本記事の escape_like() のようにエスケープ文字自体もエスケープしておけば、データに含まれていても正しく検索できます。

Q. 正規表現(REGEXP、~)とLIKEはどう使い分けますか? A. 「含む」「始まる」程度ならLIKEが読みやすく、前方一致ならインデックスも使えます。「数字が3桁続く」のような複雑な条件は正規表現が向いています。

Q. MySQL 8.4 や 9.7 でも同じ結果になりますか? A. 本記事で扱ったLIKE・ESCAPE・NO_BACKSLASH_ESCAPES・照合順序の指定方法は、8.4/9.7でも同じ手順で使えます。照合順序ごとの細かな比較結果はバージョンで変わる可能性があるため、重要な検索は利用中のバージョンで本記事のSQLを一度実行して確認すると確実です。

まとめ

  • LIKEのワイルドカードは %(0文字以上)と _(1文字)の2つ。前方・後方・部分一致は % の位置で決まる
  • % や _ を文字として探すときは ESCAPE '!' を明示すると、PostgreSQL・MySQL・SQLiteで同じ結果になる
  • 既定のエスケープ文字はPostgreSQLとMySQLが \、SQLite・SQL Server・Oracleはなし。SQL Serverは [%] も使える
  • 大文字小文字の扱いはDBごとに異なる。PostgreSQLは ILIKE、MySQLは COLLATE、SQLiteは PRAGMA で切り替える
  • アプリではプレースホルダ+エスケープ関数。エスケープ文字自体を最初に置き換える
  • インデックスが効くのは前方一致。PostgreSQLは照合順序によって text_pattern_ops が必要
  • 部分一致を速くしたいときは pg_trgm や FULLTEXT(ngram) を検討する

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「SQL LIKE部分一致とエスケープ|%・_の扱いをDB別にコピペ解説」

株式会社RJCでは、SQLの検索の書き方やDBごとの違いを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?