SQLにおいて、特定の範囲内にデータが含まれているかを判定するBETWEEN演算子は、非常に汎用性の高いツールです。
一般的には数値や日付の抽出に使用されることが多いですが、文字列データに対しても同様に適用することが可能です。
しかし、文字列の比較には辞書順(辞書式順序)という特有のルールが適用されるため、数値と同じ感覚で使用すると予期せぬ結果を招くことがあります。
本記事では、SQLのBETWEEN演算子を文字列に使用する際の基本的な仕組みから、実務で直面しやすい注意点までを詳しく説明します。
文字列におけるBETWEEN演算子の基本動作
文字列に対してBETWEEN演算子を使用する場合、データはアルファベット順や五十音順などの辞書式順序に基づいて比較されます。
例えば、column BETWEEN 'A' AND 'C'という条件を指定した場合、’A’で始まる文字列から’C’そのものまでの範囲が対象となります。
以下のサンプルコードは、商品名が格納されたテーブルから特定の範囲のデータを抽出する例です。
-- 商品テーブルから名前が 'Apple' と 'Cherry' の間のデータを抽出する
SELECT product_name
FROM products
WHERE product_name BETWEEN 'Apple' AND 'Cherry'
ORDER BY product_name;
product_name
------------
Apple
Banana
Cherry
この結果からわかる通り、’Apple’と’Cherry’、そしてその中間にある’Banana’が正しく抽出されています。
ここで重要なのは、BETWEENは開始値と終了値の両方を含む(Inclusive)という点です。
数値の範囲指定と同様に、境界値そのものも検索結果に含まれることを忘れないようにしましょう。
文字列比較における「辞書順」の落とし穴
文字列のBETWEENにおいて最も注意すべき点は、文字数ではなく文字のコードポイント順で比較が行われることです。
例えば、’B’は’Apple’よりも後ろに位置しますが、’Banana’もまた’Apple’より後ろに位置します。
辞書順では、最初の1文字目を比較し、同じであれば2文字目を比較するというプロセスが繰り返されます。
このルールにより、人間が直感的に期待する範囲と、データベースが判定する範囲にズレが生じることがあります。
終了値の指定に関する注意点
特に間違いやすいのが、終了値として指定した文字列そのものよりも「後ろ」に続く文字列の扱いです。
例えば、BETWEEN 'A' AND 'C'と指定した場合、’C’という1文字のデータは含まれますが、‘Cherry’や’Cake’といった文字列は含まれません。
なぜなら、辞書順において ‘Cake’ は ‘C’ よりも後ろ(大きい)と判定されるためです。
以下のクエリとその結果を確認してみましょう。
-- 'A' から 'C' までの範囲を指定
SELECT word
FROM vocabulary
WHERE word BETWEEN 'A' AND 'C';
word
----
A
Apple
B
Banana
C
この例では、’C’で始まる単語のうち ‘C’ 以外はすべて除外されています。
もし ‘C’ で始まるすべての単語を含めたい場合は、終了値に ‘C’ ではなく ‘D’ を指定するか、LIKE演算子を検討する必要があります。
あるいは、終了値に十分大きな値を設定するために BETWEEN 'A' AND 'CZZZ' のような指定をすることもありますが、これはあまり推奨されません。
照合順序(Collation)による挙動の違い
SQLで文字列を比較する際、データベースの照合順序(Collation)の設定が結果に大きな影響を与えます。
照合順序とは、文字の比較や並び替えのルールを定義したものです。
これには、大文字と小文字を区別するかどうか(Case Sensitivity)や、全角と半角を区別するかどうかなどが含まれます。
大文字・小文字の区別
多くのMySQLのデフォルト設定(utf8mb4_general_ciなど)では、大文字と小文字を区別しません。
この場合、’a’ も ‘A’ も同じ値として扱われるため、BETWEEN 'a' AND 'c' は ‘A’ や ‘B’ も抽出対象とします。
一方で、PostgreSQLや、バイナリ照合順序(_bin)を使用している場合は、大文字と小文字が厳密に区別されます。
ASCIIコードにおいて大文字は小文字よりも前に配置されているため、大文字と小文字が混在するデータセットでは意図しない順序になる可能性があります。
| 文字 | ASCII/Unicode値(例) | 順序のイメージ |
|---|---|---|
| A | 65 | 最初の方 |
| Z | 90 | 中間 |
| a | 97 | 大文字より後ろ |
| z | 122 | 最後の方 |
このように、使用しているデータベースエンジンのデフォルト設定を確認することは、正確なクエリを書く上で不可欠です。
実務で役立つ安全な文字列範囲指定の方法
BETWEEN演算子の曖昧さを回避し、より確実に文字列の範囲を抽出するための代替案を紹介します。
最も確実な方法は、比較演算子(>= および <)を明示的に使用することです。
例えば、’A’から’C’で始まるすべてのデータを取得したい場合は、以下のように記述するのが一般的です。
-- 'A' 以上 'D' 未満という指定で 'C' 系統をすべてカバーする
SELECT product_name
FROM products
WHERE product_name >= 'A' AND product_name < 'D';
この書き方であれば、’C’で始まるどんなに長い文字列(例:’Cranberry’)であっても、’D’よりは前であるため確実に含まれます。
境界値の扱いに迷った際は、BETWEENよりも比較演算子による明示的な範囲指定を選択することをおすすめします。
日本語(マルチバイト文字)を扱う場合
日本語のひらがなやカタカナに対してもBETWEENは機能しますが、その挙動はさらに複雑です。
濁点や半濁点の扱い、あるいは「は」と「ぱ」の順序などは、データベースの文字コードやロケール設定に依存します。
日本語環境で厳密な範囲指定が必要な場合は、事前にその環境のソート順序を十分に検証してください。
パフォーマンスへの影響
文字列のBETWEENを使用する際、対象の列にインデックスが貼られているかどうかで実行速度が大きく変わります。
基本的には、数値と同様に文字列の範囲検索でもB-treeインデックスが有効に活用されます。
ただし、前方一致(’A%’のような形式)ではなく、複雑な範囲指定や関数を適用した比較を行うと、インデックスが使用されずフルテーブルスキャンが発生する恐れがあります。
大量のデータを扱うテーブルでは、実行計画(EXPLAIN)を確認し、インデックスが適切に使用されているかをチェックしましょう。
-- インデックスが効いているか確認する例(MySQL)
EXPLAIN SELECT * FROM users WHERE username BETWEEN 'alpha' AND 'gamma';
適切なインデックス設計を行うことで、広範囲の文字列検索であっても高速なレスポンスを維持することが可能です。
まとめ
SQLのBETWEEN演算子を用いた文字列検索は、簡潔な記述で範囲を指定できる便利な手法です。
しかし、数値とは異なり、辞書順による比較や終了値の包含ルール、照合順序の影響といった特有の考慮事項が存在します。
特に「’C’で終わる範囲を指定しても’Cherry’は含まれない」という挙動は、多くの初心者が陥りやすいミスの一つです。
意図したデータを正確に取得するためには、BETWEENの特性を理解した上で、必要に応じて比較演算子(< や >=)を使い分ける柔軟性が求められます。
開発環境と本番環境で照合順序の設定が異なり、結果が変わってしまうというトラブルも珍しくありません。
常にデータのソート規則を意識しながら、安全で効率的なクエリ作成を心がけましょう。
