SQLデータベースを運用する中で、特定の文字列パターンに一致するデータを探すだけでなく、特定のパターンを「含まない」データを抽出したい場面は多々あります。
例えば、特定のドメイン以外のメールアドレスをリストアップしたり、特定のプレフィックスが付与されたテスト用のアカウントを除外したりする場合です。
このような要件を効率的に実現するために用意されているのが、LIKE演算子の否定形であるNOT LIKE演算子です。
本記事では、SQLの否定検索において基本となるNOT LIKEの使い方から、実務でハマりやすいNULL値の扱い、そしてパフォーマンス面での注意点までを詳しく紹介します。
この記事を通じて、柔軟なデータ抽出を行うためのスキルを習得していきましょう。
NOT LIKE演算子の基本的な使い方
SQLにおいて、あるカラムの値が特定のパターンに一致しないレコードを取得するには、WHERE句の中でNOT LIKEを使用します。
基本的な構文は、対象のカラム名の後にNOT LIKEを記述し、その後に比較したいパターンを記述する形式です。
-- 基本的な構文
SELECT カラム名
FROM テーブル名
WHERE カラム名 NOT LIKE 'パターン';
この演算子を使用することで、指定したパターンに合致するデータが結果セットから除外されます。
例えば、商品名のリストから「テスト」という言葉が含まれていない商品だけを表示したい場合は、以下のようなクエリを記述します。
-- 「テスト」が含まれない商品を取得する
SELECT product_id, product_name
FROM products
WHERE product_name NOT LIKE '%テスト%';
このクエリを実行すると、商品名に「テスト」という文字列が一度も出現しないレコードのみが抽出されます。
product_id | product_name
-----------+--------------
1 | プレミアムノート
2 | 高級万年筆
5 | 事務用消しゴム
このように、特定の条件をフィルタリングして除外する際に、NOT LIKEは非常に強力なツールとなります。
ワイルドカードを組み合わせた高度な否定検索
NOT LIKE演算子でも、通常のLIKE句と同様にワイルドカード文字を使用することができます。
SQLで使用される主なワイルドカードは、任意の0文字以上の文字列を表す%(パーセント)と、任意の1文字を表す_(アンダースコア)の2種類です。
任意の文字列を除外する(%)
もっとも頻繁に使用されるのが、%を用いた検索です。
例えば、特定のメールドメイン「example.com」以外のユーザーを抽出したい場合は、パターンの前方に%を付けます。
-- example.comドメイン以外のメールアドレスを抽出
SELECT user_id, email
FROM users
WHERE email NOT LIKE '%@example.com';
この場合、「user@example.com」や「admin@example.com」といった値を持つレコードが除外されます。
特定の文字数や位置を指定して除外する(_)
一方で、特定の「文字数」を指定して除外したい場合には_を活用します。
例えば、3文字の商品コードのうち、2文字目が「A」ではないものだけを取得したい場合は、以下のように記述します。
-- 2文字目が 'A' ではない3文字のコードを検索
SELECT code
FROM items
WHERE code NOT LIKE '_A_';
このクエリにより、「BAZ」や「CAT」は条件に一致しますが、「BAD」や「MAP」は除外されることになります。
NOT LIKE使用時の注意点:NULL値の扱い
SQLで否定検索を行う際、もっとも注意しなければならないのがNULL値(空の値)の挙動です。
初心者がもっとも陥りやすい罠として、「NOT LIKEを指定すれば、NULLのレコードも含まれるはずだ」という思い込みがあります。
しかし、SQLの標準的な仕様では、比較対象のカラムがNULLである場合、LIKEもNOT LIKEも「不明(UNKNOWN)」という評価になります。
その結果、NULLが含まれるレコードは検索結果に表示されません。
-- 以下のクエリでは、memoカラムがNULLのレコードは取得されません
SELECT *
FROM tasks
WHERE memo NOT LIKE '%完了%';
もし、特定のパターンを含まないデータに加えて、そもそも値が入力されていないNULLのデータも取得したい場合は、明示的にIS NULLを組み合わせる必要があります。
-- NULLのレコードも含めて否定検索を行う方法
SELECT *
FROM tasks
WHERE memo NOT LIKE '%完了%'
OR memo IS NULL;
この記述を忘れると、意図せず重要なデータが検索漏れとなってしまう可能性があるため、常に意識しておくことが重要です。
複数のNOT LIKEを組み合わせる方法
実務では、1つの条件だけでなく、複数のパターンを除外したいというケースがよくあります。
その場合は、AND演算子を使用してNOT LIKEを連結します。
-- 「テスト」も「サンプル」も含まないデータを取得
SELECT product_name
FROM products
WHERE product_name NOT LIKE '%テスト%'
AND product_name NOT LIKE '%サンプル%';
ここで初心者が間違えやすいのが、ORを使ってしまうことです。
「Aではない、またはBではない」という条件をORで繋ぐと、論理的にほとんどすべてのデータがヒットしてしまいます。
「どちらの条件も満たさないもの」を除外したい場合は、必ずANDで繋ぐという原則を覚えておきましょう。
| 条件の組み合わせ | 論理的な意味 | 一般的な用途 |
|---|---|---|
| A NOT LIKE … AND B NOT LIKE … | Aの条件もBの条件も満たさないものだけを抽出 | 複数の不要なキーワードを完全に除外したいとき |
| A NOT LIKE … OR B NOT LIKE … | AかBのどちらか一方でも条件を満たさないものを抽出 | 特殊なケースを除き、あまり使用されない |
パフォーマンスへの影響とインデックスの制約
NOT LIKEを使用する際に避けて通れないのが、パフォーマンス(実行速度)の問題です。
一般的に、SQLにおいて「否定形」の条件は、肯定形に比べてデータベースの負荷が高くなる傾向があります。
特に、B-treeインデックスが有効に活用されないという点が大きな懸念材料です。
通常のLIKE 'パターン%'(前方一致)であればインデックスが効く場合がありますが、NOT LIKEの場合は基本的にテーブルの全レコードをスキャンする「フルテーブルスキャン」が発生します。
データ量が数万件程度であれば大きな問題にはなりませんが、数百万件、数千万件という大規模なテーブルに対してNOT LIKEを多用すると、レスポンスが極端に悪化する恐れがあります。
パフォーマンスを改善するためには、以下のような対策を検討してください。
- 可能であれば、否定ではなく「肯定(IN句やLIKE句)」の条件に書き換えられないか検討する。
- 除外したいパターンが決まっている場合は、フラグカラム(is_deletedなど)を作成し、その値をインデックス付きで検索する。
- 全文検索エンジン(ElasticsearchやGroongaなど)の導入を検討する。
エスケープ処理が必要なケース
検索したい文字列自体に、ワイルドカード文字である%や_が含まれている場合、そのまま記述すると正しく検索できません。
例えば、「100%」という文字列を含まないデータを検索したい場合、単にNOT LIKE '%100%%'と書くと、最後の%がワイルドカードとして解釈されてしまいます。
このようなときは、ESCAPE句を使用して特定の文字をエスケープ文字として定義します。
-- '$' をエスケープ文字として定義し、'%' 自体を検索する
SELECT *
FROM campaign
WHERE description NOT LIKE '%100$%%' ESCAPE '$';
このように記述することで、$%の部分が「文字としてのパーセント」として正しく扱われるようになります。
記号を含む複雑なデータを扱うシステムでは、このエスケープ処理を適切に行うことがデータ整合性を守る鍵となります。
まとめ
SQLにおけるNOT LIKE演算子は、特定のパターンを除外して必要なデータだけを絞り込むための非常に便利な手段です。
基本となるワイルドカード(%や_)の使い方をマスターすれば、複雑なフィルタリングも容易に行えるようになります。
しかし、実務で使用する際には、NULL値を持つレコードが自動的に除外されてしまうという仕様に十分注意しなければなりません。
また、大規模なテーブルに対してはフルテーブルスキャンによるパフォーマンス低下のリスクがあるため、実行計画を確認しながら慎重に使用することが推奨されます。
今回解説したポイントを押さえて、より正確で効率的なSQLクエリの作成に役立ててください。
