【SQL BETWEEN句】範囲検索をマスター!数値や日付の範囲指定をスマートに書く方法
Webサイトやアプリケーションで、「価格が1,000円以上、5,000円以下の商品」「6月1日から6月30日までに登録したユーザー」のように、ある範囲に収まるデータを抽出したいケースは非常に多くあります。
このような条件をWHERE句で書くとき、あなたならどうしますか?
WHERE price >= 1000 AND price <= 5000
このように、比較演算子(>=, <=)とANDを使えば、もちろん範囲を指定できます。しかし、SQLにはこの「範囲検索」をもっと直感的で、スッキリと書くための専用の命令が用意されています。それが今回主役の`BETWEEN`句です。
この記事では、BETWEEN句の基本的な使い方から、日付範囲での応用、そして否定形のNOT BETWEENまで、コピペで動くコードと共に徹底解説します。比較演算子での冗長な書き方から卒業し、誰が見ても分かりやすいスマートなSQLを書くスキルを身につけましょう!
準備:検索対象の注文データを用意しよう
範囲検索を試すために、まずは検索対象となるデータが必要です。今回は、ECサイトの注文履歴を模したordersテーブルを作成します。様々な価格帯や注文日のデータを含めておくことで、BETWEEN句の効果が実感しやすくなります。
-- もしordersテーブルが存在すれば削除(繰り返し試せるように)
DROP TABLE IF EXISTS orders;
-- 新しいordersテーブルを作成
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
product_name TEXT NOT NULL,
price INTEGER NOT NULL,
order_date DATE NOT NULL,
quantity INTEGER NOT NULL
);
-- 初期データを挿入
INSERT INTO orders (id, product_name, price, order_date, quantity) VALUES
(1, 'すごいボールペン', 200, '2025-06-05', 3),
(2, '魔法のノート', 500, '2025-06-10', 5),
(3, '究極の炊飯器', 30000, '2025-06-15', 1),
(4, '奇跡のリンゴ', 980, '2025-06-20', 10),
(5, 'USB-Cハブ', 4500, '2025-06-25', 2),
(6, '伝統の筆', 12000, '2025-06-30', 1),
(7, '高級チョコレート', 3500, '2025-07-01', 4);
これで、様々な範囲を指定してデータを抽出する準備が整いました。
【基本】`BETWEEN`句で数値の範囲を指定する
`BETWEEN`句の基本的な構文は非常に明快です。
WHERE 列名 BETWEEN 最小値 AND 最大値;
ここで最も重要なポイントは、`BETWEEN`句の範囲には、指定した最小値と最大値そのものが含まれる(以上・以下)ということです。つまり、WHERE price BETWEEN 1000 AND 5000は、WHERE price >= 1000 AND price <= 5000と全く同じ意味になります。この「両端を含む」という点をしっかり覚えておきましょう。
シナリオ:「価格が1,000円から5,000円の間の商品をすべて探したい」
SELECT * FROM orders
WHERE price BETWEEN 1000 AND 5000;
実行結果:
id | product_name | price | order_date | quantity
---|----------------|-------|------------|----------
5 | USB-Cハブ | 4500 | 2025-06-25 | 2
7 | 高級チョコレート | 3500 | 2025-07-01 | 4
比較演算子を2つ使うよりも、コードがスッキリして「1,000円から5,000円の範囲」という意図がストレートに伝わりますね。
【応用】`BETWEEN`句で日付の範囲を指定する
`BETWEEN`句の便利な点は、数値だけでなく日付(DATE型)や日時(DATETIME型)の範囲指定にも使えることです。Webアプリケーションでは、特定の期間のデータを抽出する要件が非常に多いため、これは頻出のテクニックです。
シナリオ:「2025年6月中に発生した注文をすべて探したい」
この場合、範囲の開始を「2025-06-01」、終了を「2025-06-30」として指定します。
SELECT * FROM orders
WHERE order_date BETWEEN '2025-06-01' AND '2025-06-30';
実行結果:
id | product_name | price | order_date | quantity
---|----------------|-------|------------|----------
1 | すごいボールペン | 200 | 2025-06-05 | 3
2 | 魔法のノート | 500 | 2025-06-10 | 5
3 | 究極の炊飯器 | 30000 | 2025-06-15 | 1
4 | 奇跡のリンゴ | 980 | 2025-06-20 | 10
5 | USB-Cハブ | 4500 | 2025-06-25 | 2
6 | 伝統の筆 | 12000 | 2025-06-30 | 1
ご覧の通り、6月1日と6月30日の注文もちゃんと結果に含まれています。これも「両端を含む」という`BETWEEN`句の性質のおかげです。
【逆の条件】`NOT BETWEEN`句で範囲外のものを指定する
`BETWEEN`の逆、つまり「指定した範囲の**外側**にあるもの」を探したい場合は、NOT BETWEEN句を使います。
ここで注意点が一つ。price BETWEEN 1000 AND 5000が「1000以上かつ5000以下」だったのに対し、price NOT BETWEEN 1000 AND 5000は「1000**未満**または5000**より大きい**」(price < 1000 OR price > 5000)という意味になります。つまり、範囲の両端の値は結果に含まれません。
シナリオ:「価格が非常に安い(1,000円未満)か、非常に高い(5,000円より大きい)商品をすべて探したい」
SELECT * FROM orders
WHERE price NOT BETWEEN 1000 AND 5000;
実行結果:
id | product_name | price | order_date | quantity
---|----------------|-------|------------|----------
1 | すごいボールペン | 200 | 2025-06-05 | 3
2 | 魔法のノート | 500 | 2025-06-10 | 5
3 | 究極の炊飯器 | 30000 | 2025-06-15 | 1
4 | 奇跡のリンゴ | 980 | 2025-06-20 | 10
6 | 伝統の筆 | 12000 | 2025-06-30 | 1
価格が1,000円と5,000円の商品はサンプルにありませんが、もし存在したとしても、この結果には含まれないことを覚えておきましょう。
【体験コーナー】ブラウザでSQLを動かし、範囲検索をマスターしよう!
さあ、知識をスキルに変える時間です! 以下のHTMLコードを丸ごとコピーして、sql_between_test.htmlのようなファイル名で保存し、ブラウザで開いてみてください。あなた専用のSQL実行環境が立ち上がり、この記事で使っているordersテーブルが用意されています。
数値や日付の範囲を色々と変えて、BETWEEN句の「両端を含む」という感覚を掴んでみてください。NOT BETWEENも試して、結果がどう変わるか確かめてみましょう!
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SQL BETWEEN句 オンライン実行環境</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; line-height: 1.7; color: #333; max-width: 800px; margin: 2rem auto; padding: 0 1rem; }
h1 { color: #16a085; }
textarea { width: 100%; height: 180px; font-family: "SF Mono", "Consolas", monospace; font-size: 16px; padding: 12px; border: 1px solid #ccc; border-radius: 6px; box-sizing: border-box; margin-bottom: 1rem; }
button { background-color: #1abc9c; color: white; border: none; padding: 12px 22px; font-size: 16px; border-radius: 6px; cursor: pointer; transition: background-color 0.2s; }
button:hover { background-color: #16a085; }
button:disabled { background-color: #bdc3c7; cursor: not-allowed; }
#result-container { margin-top: 2rem; border: 1px solid #ddd; padding: 1rem; border-radius: 6px; background: #fdfdfd; min-height: 50px; }
#error-message { color: #e74c3c; font-weight: bold; }
table { border-collapse: collapse; width: 100%; margin-top: 1rem; }
th, td { border: 1px solid #ddd; padding: 10px; text-align: left; }
th { background-color: #f2f2f2; }
tr:nth-child(even) { background-color: #f9f9f9; }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.10.3/sql-wasm.js"></script>
</head>
<body>
<h1>SQLを試してみよう!</h1>
<p>下のテキストエリアにSQL文を入力して「実行」ボタンを押してください。色々な範囲を試してみましょう!</p>
<textarea id="sql-input">-- 6月10日から6月25日の間に注文された商品を探す
SELECT * FROM orders
WHERE order_date BETWEEN '2025-06-10' AND '2025-06-25';
</textarea>
<button id="execute-btn">実行</button>
<div id="result-container">
<p id="error-message"></p>
<div id="result-output"></div>
</div>
<script>
const sqlInput = document.getElementById('sql-input');
const executeBtn = document.getElementById('execute-btn');
const errorMsg = document.getElementById('error-message');
const resultOutput = document.getElementById('result-output');
let db;
async function initDb() {
executeBtn.disabled = true;
executeBtn.textContent = 'DB準備中...';
try {
const SQL = await initSqlJs({
locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.10.3/${file}`
});
db = new SQL.Database();
const setupSql = `
DROP TABLE IF EXISTS orders;
CREATE TABLE orders (id INTEGER PRIMARY KEY, product_name TEXT NOT NULL, price INTEGER NOT NULL, order_date DATE NOT NULL, quantity INTEGER NOT NULL);
INSERT INTO orders (id, product_name, price, order_date, quantity) VALUES
(1, 'すごいボールペン', 200, '2025-06-05', 3),
(2, '魔法のノート', 500, '2025-06-10', 5),
(3, '究極の炊飯器', 30000, '2025-06-15', 1),
(4, '奇跡のリンゴ', 980, '2025-06-20', 10),
(5, 'USB-Cハブ', 4500, '2025-06-25', 2),
(6, '伝統の筆', 12000, '2025-06-30', 1),
(7, '高級チョコレート', 3500, '2025-07-01', 4);
`;
db.run(setupSql);
executeBtn.disabled = false;
executeBtn.textContent = '実行';
resultOutput.innerHTML = '<p>準備完了!自由にSQLを試してみてください。</p>';
} catch (err) {
errorMsg.textContent = 'データベースの初期化に失敗しました: ' + err.message;
console.error(err);
}
}
function executeSql() {
if (!db) return;
const sql = sqlInput.value;
errorMsg.textContent = '';
resultOutput.innerHTML = '';
try {
const results = db.exec(sql);
if (results.length === 0) {
resultOutput.innerHTML = '<p>クエリは成功しましたが、結果セットは返されませんでした。</p>';
return;
}
results.forEach(result => {
const table = document.createElement('table');
const thead = document.createElement('thead');
const tbody = document.createElement('tbody');
const headerRow = document.createElement('tr');
result.columns.forEach(colName => {
const th = document.createElement('th');
th.textContent = colName;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
result.values.forEach(row => {
const bodyRow = document.createElement('tr');
row.forEach(cellValue => {
const td = document.createElement('td');
td.textContent = cellValue === null ? 'NULL' : cellValue;
bodyRow.appendChild(td);
});
tbody.appendChild(bodyRow);
});
table.appendChild(thead);
table.appendChild(tbody);
resultOutput.appendChild(table);
});
} catch (err) {
errorMsg.textContent = 'SQLエラー: ' + err.message;
console.error(err);
}
}
executeBtn.addEventListener('click', executeSql);
initDb();
</script>
</body>
</html>
気をつけるべき点:日時の扱いと値の順序
BETWEEN句はシンプルで強力ですが、いくつか注意すべき点があります。
- 値の順序:
BETWEEN A AND Bと書く場合、必ずAに小さい方の値、Bに大きい方の値を指定する必要があります。もしBETWEEN 5000 AND 1000のように順序を逆にしてしまうと、たとえその範囲にデータが存在していても、1件もヒットしなくなってしまいます。常に「最小値 AND 最大値」の順で書くことを徹底しましょう。 - 日時(TIMESTAMP型など)の扱い: 日付だけでなく、時刻まで含む列(`YYYY-MM-DD HH:MM:SS`)に対して
BETWEENを使う場合は特に注意が必要です。例えば、order_datetime BETWEEN '2025-06-01' AND '2025-06-30'と指定した場合、これは内部的に'2025-06-30 00:00:00'までと解釈されます。そのため、6月30日の午後に行われた注文(例: '2025-06-30 15:00:00')は範囲外となり、検索結果に含まれません。6月30日全体を含めたい場合は、BETWEEN '2025-06-01 00:00:00' AND '2025-06-30 23:59:59'のように時刻まで明示的に指定するか、比較演算子を使ってorder_datetime >= '2025-06-01' AND order_datetime < '2025-07-01'のように「翌日の0時より前」と指定するのが確実です。
まとめ
今回は、数値や日付の範囲検索を直感的に書けるBETWEEN句について学びました。
- 基本構文:
WHERE 列名 BETWEEN 最小値 AND 最大値。比較演算子(>= ... AND <= ...)よりも可読性が高い。 - 範囲は両端を含む: `BETWEEN`句は「以上・以下」で評価される、最も重要なポイント。
- `NOT BETWEEN`: 範囲外のデータを抽出する。こちらは「未満・より大きい」で評価され、両端を含まない。
- 日付にも使える:
'YYYY-MM-DD'形式の文字列を指定することで、日付の範囲検索も簡単に行える。 - 注意点: 常に「最小値 AND 最大値」の順で書くことと、時刻を含むデータの範囲指定には工夫が必要なことを覚えておく。
BETWEEN句を使いこなせば、あなたの書くSQLはよりシンプルで、意図が伝わりやすいものになります。単純な比較演算子の組み合わせから一歩進んで、より洗練されたデータ抽出のテクニックをあなたの道具箱に加えましょう!