目次
はじめに
データベースを使うときに最も重要な操作のひとつが「検索」です。
検索を正しく使えるようになると、欲しいデータを自由自在に取り出せるようになります。
この記事では、SQL(Structured Query Language)の中でも、データ検索に焦点をあてて解説します。
初心者の方でも理解できるように、できるだけわかりやすく表形式や例を用いて解説します。
ちなみに、どのSQLでも基本は同じです。
プログラミング言語より基本部分は簡単ですので、怖がらずに使ってみると良いでしょう。
そもそもSQLって何さ?という方はこちらの記事を先にお読みください。
SQLでできる検索の種類
SQLを使うと、以下のような検索が可能になります。
検索内容 | 例 |
---|---|
特定の列だけ取得 | 名前とメールアドレスだけ取り出す |
条件付き取得 | 年齢が30歳以上の人だけ取り出す |
重複排除 | 同じ部署名を一度だけ表示 |
並び替え | 成績順に昇順や降順に並べる |
部分一致検索 | 名前に「山」が含まれる人を探す |
NULLチェック | 電話番号が未登録の人を探す |
日付検索 | 2024年1月以降に登録したデータだけ取り出す |
SELECT文の基本構造
SQLで検索するときの基本文法は SELECT文 です。
構造はとてもシンプルで、次の3つの要素から成り立っています。
句 | 役割 |
---|---|
SELECT | 取り出したい列(カラム)を指定する |
FROM | どの表(テーブル)からデータを取得するか指定する |
WHERE | 条件を指定する(必要に応じて省略可能) |
SELECT句とアスタリスク(*)
一番簡単な検索は以下のようになります。
SELECT * FROM employees;
これは「employeesテーブルからすべての列を取得する」という意味です。
この *
を ワイルドカード と呼びます。
書き方 | 意味 |
---|---|
SELECT * | 全ての列を取得 |
SELECT name, age | name列とage列だけ取得 |
※ 実務では不要な列まで取るとパフォーマンスに悪影響が出るので、必要な列を指定するのが望ましいです。
射影演算(必要な列だけ取得)
射影演算とは「必要な列だけを取り出す」ことです。
SELECT name, department FROM employees;
これにより、名前と部署だけを取り出せます。
たとえば、次のような結果になります。
name | department |
---|---|
山田太郎 | 営業 |
佐藤花子 | 開発 |
鈴木一郎 | 人事 |
計算結果の表示
SQLでは列の値を使って計算した結果を表示することもできます。
SELECT name, salary, salary * 12 AS annual_salary
FROM employees;
name | salary | annual_salary |
---|---|---|
山田太郎 | 300000 | 3600000 |
佐藤花子 | 280000 | 3360000 |
ここで使った AS は「別名をつける」キーワードです。salary * 12
という計算式に annual_salary
というわかりやすい名前をつけています。
DISTINCT(重複の排除)
データベースには同じ値が何度も出てくることがあります。
その場合に「重複を排除」して表示するのが DISTINCT です。
SELECT DISTINCT department FROM employees;
WHERE句と条件検索
検索の本領は WHERE句 にあります。条件を指定することで、欲しいデータだけを取り出せます。
比較演算子
演算子 | 意味 | 例 |
---|---|---|
= | 等しい | age = 30 |
<> または != | 等しくない | department <> ‘営業’ |
> | より大きい | salary > 300000 |
< | より小さい | salary < 200000 |
>= | 以上 | age >= 20 |
<= | 以下 | age <= 65 |
論理演算子(AND, OR, NOT)
演算子 | 意味 | 例 |
---|---|---|
AND | 両方の条件を満たす | age > 20 AND department = ‘営業’ |
OR | どちらかの条件を満たす | department = ‘営業’ OR department = ‘開発’ |
NOT | 条件を満たさない | NOT department = ‘営業’ |
BETWEEN(範囲検索)
SELECT name, age FROM employees
WHERE age BETWEEN 20 AND 30;
20歳以上30歳以下の社員を検索します。
IS NULL / IS NOT NULL
データベースでは「値が入っていない」状態を NULL と呼びます。
SELECT name FROM employees
WHERE phone IS NULL;
電話番号が未登録の社員を取得します。
IN(リスト指定)
SELECT name FROM employees
WHERE department IN ('営業', '人事');
営業部か人事部に所属している人を取得します。
LIKE(部分一致検索)
部分一致を探すときに使います。
パターン | 意味 |
---|---|
‘山%’ | 「山」で始まる |
‘%子’ | 「子」で終わる |
‘%田%’ | 「田」を含む |
SELECT name FROM employees
WHERE name LIKE '%田%';
正規表現(REGEXP)
もっと柔軟に文字列検索する場合、正規表現を使います。
ちなみに正規表現と難しい言葉ですが、ある文字列を簡単に表す手法です。
つまり、いくつかの文字列を一つの文字列で表すことができる方法ということです。
もっとぶっちゃけて言うと、長々とコードを打つのが面倒くさいので簡単に短くする方法です。(一括検索)
とてもあいまいな感じでも検索してくれる使い勝手の良いものです。
SELECT name FROM employees
WHERE name REGEXP '^山.*郎$';
「山」で始まり「郎」で終わる名前を検索します。
※正規表現が気になった人は「まとめ」の下に続くおまけ記事読んでください。
日付検索
日付を条件に検索することもよくあります。
SELECT name FROM employees
WHERE hire_date >= '2023-01-01';
ORDER BY(並び替え)
結果を昇順や降順に並び替えることができます。
SELECT name, salary FROM employees
ORDER BY salary DESC;
NULLの扱い
NULLは「値が存在しない」状態を意味します。
= NULL
では判定できず、必ず IS NULL
を使います。
また、計算にNULLが含まれると結果もNULLになります。
まとめ
SQLでデータ検索を行う際の基本は次の3つです。
- SELECT(取得列の指定)
- FROM(対象のテーブル指定)
- WHERE(条件指定)
さらに応用として、DISTINCT・AS・比較演算子・論理演算子・LIKE・IN・BETWEEN・NULL・ORDER BY などを組み合わせることで、自由度の高い検索が可能になります。
普通に使う分には、プログラミング言語系より遥かに簡単です。(ガッツリ使うと迷走する傾向にあるけど)
実際の業務では、「欲しいデータを正しく・効率よく取り出す」ことが最も重要です。
SQL検索をマスターすれば、データ分析・システム開発・レポート作成など、あらゆる場面で役立ちます。
データベース系をがっつり学びたいなら、ここが一番おススメです。

エンジニア系のスクールならどこでもSQLの科目はひっついてきます。(ただ、内容が浅いです)インターネットアカデミーさんは専門的なとこまで教えてくださいます。
オススメなのですが、IT業界以外で使うなら独学でもOKです。このサイトに書いてあることぐらいしか使わないです。(今後もシリーズ化して出していくので)
こっから下は正規表現が気になった人は見てねというおまけ部分
主要構文の対応早見表
構文/機能 | MySQL | PostgreSQL | Oracle | SQL Server |
---|---|---|---|---|
アンカー ^ $ | ○ | ○ | ○ | △(LIKEなし) |
量指定 {n,m} | ○ | ○ | ○ | × |
代替 `(a | b)` | ○ | ○ | ○ |
後方参照 \1 | ○ | ○ | ○ | × |
前/後読み | ○ | ○ | ○ | × |
\d \w \s | ○(ICU) | ○(互換/ARE) | △(基本はPOSIX推奨) | × |
POSIX [[:digit:]] 等 | ○ | ○ | ○ | × |
大小無視フラグ | i / ~ | ~* or (?i) | i | COLLATE で近似 |
置換関数 | REGEXP_REPLACE | regexp_replace | REGEXP_REPLACE | ×(CLR等) |
注:DBやバージョン・照合順序で細かい差があります。移植性を優先するなら POSIX 文字クラスを使うのが無難です。
MySQL 8.0 正規表現 早見表(ICU ベース)
1) 使える演算子・関数と書式
種別 | シグネチャ | よく使う例 | 補足 |
---|---|---|---|
演算子 | expr REGEXP pat (= RLIKE ) | name REGEXP '^[A-Z]+' | ブール返却(TRUE/FALSE) |
判定 | REGEXP_LIKE(expr, pat [, match_type]) | REGEXP_LIKE(email, '\\.jp$', 'i') | 大小無視など細かく制御 |
置換 | REGEXP_REPLACE(expr, pat, repl [, pos, occ, match_type]) | REGEXP_REPLACE(txt, '\\s+', ' ') | 置換後文字列を返す |
抽出 | REGEXP_SUBSTR(expr, pat [, pos, occ, match_type]) | REGEXP_SUBSTR(url, 'https?://[^/]+') | パターンに一致した部分文字列 |
位置 | REGEXP_INSTR(expr, pat [, pos, occ, return_opt, match_type]) | REGEXP_INSTR(txt, '\\d+') | マッチの開始/終了位置を返す |
match_type(フラグ)
i
:大小無視(case-insensitive)c
:大小区別(明示)m
:複数行モード(^
/$
が行頭/行末)n
:dotall(.
が改行にもマッチ)
REGEXP_REPLACE(col, pat, repl, 1, 1, 'im')
のように複数指定可。
2) パターン構文チートシート(ICU準拠)
基本・量指定・アンカー
構文 | 意味 | 例 |
---|---|---|
. | 任意1文字 | a.c → abc |
* / + / ? | 0回以上 / 1回以上 / 0か1回 | \\d+ |
{n} / {n,} / {n,m} | ちょうど / 以上 / n〜m回 | a{2,4} |
^ / $ | 文字列(行)の先頭/末尾 | ^hello$ |
文字クラス
構文 | 意味 | 例 |
---|---|---|
[abc] / [^abc] | いずれか1文字 / 以外 | [A-F0-9] |
範囲 | a-z A-Z 0-9 | [A-Za-z0-9] |
POSIX | [[:digit:]] [[:alpha:]] [[:alnum:]] [[:space:]] [[:upper:]] [[:lower:]] | [[:digit:]]{3} |
ショートカット | \\d 数字 / \\w 単語 / \\s 空白 | \\w+ |
グルーピング・選択・参照
構文 | 意味 | 例 |
---|---|---|
( … ) | 捕捉グループ | `(19 |
(?: … ) | 非捕捉グループ | `(?:jpg |
a|b | 選択(OR) | (foo|bar) |
\\1 など | 後方参照 | (\\w+)\\s+\\1 |
単語境界・先読み/後読み
構文 | 意味 | 例 |
---|---|---|
\\b / \\B | 単語境界/非境界 | \\bcat\\b |
(?=...) / (?!...) | 先読み(肯定/否定) | foo(?=bar) |
(?<=...) / (?<!...) | 後読み(肯定/否定) | (?<=ID:)\\d+ |
後読みは固定長のみ(可変長は不可)を意識してください。
3) 実務レシピ集(すぐ使える)
A. バリデーション
-- 半角英数字とハイフンのみ
SELECT *
FROM users
WHERE REGEXP_LIKE(username, '^[A-Za-z0-9-]+$');
-- 日本の郵便番号 123-4567
SELECT *
FROM addresses
WHERE REGEXP_LIKE(zip, '^[0-9]{3}-[0-9]{4}$');
-- メール(簡易形)
SELECT *
FROM users
WHERE REGEXP_LIKE(email, '^[^@\\s]+@[^@\\s]+\\.[A-Za-z]{2,}$', 'i');
B. マスキング・正規化
-- 電話番号のハイフン以外を除去 → 数字だけ
SELECT REGEXP_REPLACE('TEL: 03-1234-5678', '[^0-9]', '') AS digits;
-- 連続空白を1つに正規化
SELECT REGEXP_REPLACE(notes, '\\s+', ' ') AS normalized
FROM memos;
-- メールローカル部を部分マスク
SELECT REGEXP_REPLACE(email, '(^.).*?(@)', '\\1***\\2') AS masked
FROM users;
C. 抽出・分解・位置
-- URL からホスト部を抽出
SELECT REGEXP_SUBSTR(url, 'https?://([^/]+)', 1, 1, NULL) AS full_host,
REGEXP_SUBSTR(url, 'https?://([^/]+)', 1, 1, NULL, 1) AS host_only -- 1番目のグループ
FROM pages;
-- N 番目の数値トークンを抽出(occurrence を使う)
SELECT
REGEXP_SUBSTR(txt, '\\d+', 1, 1) AS first_num,
REGEXP_SUBSTR(txt, '\\d+', 1, 2) AS second_num
FROM t;
-- 一致位置(開始位置)を取得
SELECT REGEXP_INSTR(txt, '\\d+') AS first_digit_pos
FROM t;
D. フィルタリング(SELECT)
-- 先頭が A〜F の顧客
SELECT * FROM customers
WHERE name REGEXP '^[A-F]';
-- 行末が .jp または .com
SELECT * FROM users
WHERE REGEXP_LIKE(email, '\\.(jp|com)$', 'i');
-- 「foo」の直後に「bar」が続く
SELECT * FROM logs
WHERE REGEXP_LIKE(message, 'foo(?=bar)');
4) よくある日本語テキストの注意点
- バックスラッシュのエスケープ
SQL 文字列内の\
は\\
と2重に書きます。例:'\\d+'
,'\\b'
。 - 全角・半角の扱い
\\d
は Unicode の数字(他言語の数字も含む)にマッチしうる点に注意。ASCII のみに限定したいなら[0-9]
または[[:digit:]]
を使う。- 英字限定は
[A-Za-z]
、日本語を含む「文字」は\\w
だと広く取りすぎることがあります(記号も含みうる)。用途に応じて明示的なクラスを。
- 単語境界
\\b
日本語など空白で区切られないスクリプトでは期待どおりに働かないケースがあります。語彙境界を正確に扱う必要がある場合は明示的な前後条件(前読み/後読み)で囲う方が安全です。 - 照合順序(照合順序 vs REGEXP)
REGEXP_*
は ICU 正規表現の評価で、データベースの照合順序 (collation) の影響を受けません。大小無視はi
フラグで行います。
5) パフォーマンス最適化のコツ
- アンカーを付ける:
^
/$
を付けて走査範囲を短く。 - 前方一致は LIKE で前絞り:
WHERE col LIKE 'foo%' AND REGEXP_LIKE(col, '...')
。 - 否定の巨大クラスを避ける:
[^...]
よりも肯定の最小集合に寄せる。 - 必要最小限の幅:
.*
ではなく[^/]*
のように文脈に応じて狭める。 - 頻出パターンは生成列 + インデックス:例)
generated column
に正規化して B-Tree インデックスを貼る。
6) 置換の後方参照(よく使う)
-- キャプチャした順に \\1, \\2 ...
SELECT REGEXP_REPLACE('2025/08/22', '^(\\d{4})/(\\d{2})/(\\d{2})', '\\1-\\2-\\3');
-- ケバブ → スネーク
SELECT REGEXP_REPLACE('user-profile-name', '-', '_');
7) 代表的パターン集(コピー用)
目的 | パターン | 備考 |
---|---|---|
数字のみ | ^[0-9]+$ | 全角数字を許容なら ^[[:digit:]]+$ |
英数字 | ^[A-Za-z0-9]+$ | 記号不可 |
スラッグ | ^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$ | 先頭末尾ハイフン禁止 |
日本郵便番号 | ^[0-9]{3}-[0-9]{4}$ | 123-4567 |
IPv4 | `^(?:25[0-5] | 2[0-4]\d |
URL(緩め) | ^https?://[^\\s/$.?#].[^\\s]*$ | ざっくり判定 |
メール(緩め) | ^[^@\\s]+@[^@\\s]+\\.[A-Za-z]{2,}$ | 実運用はさらに検証を |
先読み例 | foo(?=bar) | 「foo」の後ろが「bar」 |
後読み例 | (?<=ID:)\\d+ | 「ID:」の直後の数字 |
参考:よくある引っかかり(FAQ)
- 「後読みがエラー」 → 可変長にしていませんか? 後読みは固定長のみ。
- 「
.
が改行にマッチしない」 →match_type
に'n'
を付与(dotall)。 - 「大小無視したい」 →
match_type
に'i'
。LOWER()
との併用は不要。
各データベースの差分
主要4つのRDBMS(MySQL / PostgreSQL / Oracle / SQL Server)ごとに、「使える演算子・関数」「よく使う正規表現構文」「フラグ(オプション)」を一覧化しました。実務で迷いやすい差分も一緒に押さえています。
MySQL(8.0+)
主な演算子 / 関数
種別 | 書き方 | 例 |
---|---|---|
演算子 | col REGEXP 'pat' (RLIKE 同義) | name REGEXP '^[A-Z]+' |
関数 | REGEXP_LIKE(col, pat [, match_type]) | REGEXP_LIKE(email, '\\.com$', 'i') |
置換 | REGEXP_REPLACE(col, pat, repl [, pos, occ, match_type]) | REGEXP_REPLACE(t, '[^0-9]', '') |
抽出 | REGEXP_SUBSTR(col, pat [, pos, occ, match_type]) | REGEXP_SUBSTR(url, 'https?://[^/]+') |
位置 | REGEXP_INSTR(col, pat [, pos, occ, ret, match_type]) | REGEXP_INSTR(txt, '\\d+') |
よく使うパターン(ICU準拠)
構文 | 意味 | 例 |
---|---|---|
. | 任意1文字 | a.c → abc |
* + ? {n} {n,} {n,m} | 繰り返し | \\d{3,4} |
^ $ | 先頭/末尾 | ^foo$ |
[...] [^...] | 文字クラス/否定 | [A-F0-9] |
`(a | b)` | 選択 |
(?:...) | 非捕捉グループ | `(?:19 |
\\d \\w \\s | 数字/単語/空白 | \\d+ |
\\b \\B | 単語境界/非境界 | \\bcat\\b |
POSIX: [[:digit:]] 等 | 互換的で安全 | [[:alpha:]]+ |
後方参照 \\1 | 直前のグループ再利用 | (\\w+)\\s+\\1 |
フラグ(match_type
)
文字 | 効果 |
---|---|
i | 大文字小文字を無視 |
c | 大文字小文字を区別(明示) |
m | 複数行(^ /$ が行頭/行末) |
n | ドットが改行にもマッチ(dotall) |
例
-- 英数字とハイフンのみ
SELECT * FROM users WHERE REGEXP_LIKE(username, '^[A-Za-z0-9-]+$');
-- 数字以外を除去
SELECT REGEXP_REPLACE('TEL: 03-1234-5678', '[^0-9]', '') AS digits;
PostgreSQL
主な演算子 / 関数
種別 | 書き方 | 例 |
---|---|---|
演算子(区別) | ~ / !~ | email ~ '\\.com$' |
演算子(無視) | ~* / !~* | name ~* '^a' |
抽出 | substring(col FROM 'pat') | substring(url FROM 'https?://[^/]+') |
マッチ全取得 | regexp_matches(col, 'pat' [, 'g']) | regexp_matches(t, '(\\d+)', 'g') |
置換 | regexp_replace(col, 'pat', 'repl' [, 'flags']) | regexp_replace(txt, '\\s+', ' ', 'g') |
分割 | regexp_split_to_table/array | regexp_split_to_table(tags, '\\s*,\\s*') |
LIKE上位互換 | SIMILAR TO | `x SIMILAR TO ‘(foo |
よく使うパターン(ARE準拠)
構文 | 意味 |
---|---|
. * + ? {n} {n,} {n,m} / ^ $ / [...] [^...] / `(a | b)/ (?:…)/ 後方参照 \1` |
\\d \\w \\s (互換モード)とPOSIX [[:digit:]] 等 | |
(?=...) (?!...) (?<=...) (?<!...) 前/後読み(サポートあり) |
フラグ
方式 | 指定 | 例 |
---|---|---|
演算子で | ~* (無視) | name ~* '^a' |
関数フラグ | 'g' 'i' 'm' 'n' など | regexp_replace(t,'\\s+',' ','gi') |
インライン | (?i) (?m) (?n) | col ~ '(?i)^abc' |
例
-- 末尾が .jp または .com
SELECT * FROM users WHERE email ~ '\\.(jp|com)$';
-- 連続空白を1つに
SELECT regexp_replace(txt, '\\s+', ' ', 'g') FROM notes;
Oracle Database
主な関数
種別 | 書き方 | 例 |
---|---|---|
判定 | REGEXP_LIKE(col, pattern [, match_param]) | REGEXP_LIKE(email, '\\.com$', 'i') |
置換 | REGEXP_REPLACE(col, pat, repl [, pos, occ, match_param]) | 省略可 |
抽出 | REGEXP_SUBSTR(col, pat [, pos, occ, match_param]) | 省略可 |
位置 | REGEXP_INSTR(col, pat [, pos, occ, ret, match_param]) | 省略可 |
カウント | REGEXP_COUNT(col, pat [, pos, match_param]) | 出現回数 |
よく使うパターン(POSIX ERE + Oracle拡張)
構文 | 意味 |
---|---|
基本:`., * + ? {n} {n,} {n,m} ^ $ […] [^…] (a | b)` |
グループ:(...) 、非捕捉は 未サポート((?:...) なし) | |
後方参照:\\1 \\2 ... | |
POSIX文字クラス:[[:digit:]] [[:alpha:]] [[:alnum:]] [[:space:]] [[:upper:]] [[:lower:]] | |
前/後読み:(?=...) (?!...) (?<=...) (?<!...) (サポート) |
フラグ(match_param
)
文字 | 効果 |
---|---|
i | 大文字小文字を無視 |
c | 大文字小文字を区別 |
n | ドットが改行にもマッチ(dotall) |
m | 複数行 |
x | 可読性(空白/コメントを無視) |
例
-- 郵便番号(日本: 123-4567)
SELECT REGEXP_LIKE(zip, '^[0-9]{3}-[0-9]{4}$') FROM addresses;
-- ドメイン抽出
SELECT REGEXP_SUBSTR(url, 'https?://([^/]+)', 1, 1, NULL, 1) AS host FROM pages;
SQL Server(T-SQL)
SQL Server は正規表現をネイティブにサポートしていません(※LIKE
/PATINDEX
の疑似パターンのみ)。
.NET Regex を使うには CLR 統合や外部言語(SQL Server Machine Learning Services など)を利用します。
LIKE / PATINDEX のパターン
構文 | 意味 | 例 |
---|---|---|
% | 0文字以上 | col LIKE 'ab%cd' |
_ | 任意1文字 | col LIKE 'A_C' |
[abc] | いずれか1文字 | LIKE '[abc]%' |
[a-c] | 範囲1文字 | LIKE '[A-Z]%' |
[^abc] | 指定以外の1文字 | LIKE '[^0-9]%' |
エスケープ | ESCAPE で指定 | LIKE '%\%%' ESCAPE '\' |
CLR を使ったRegex(方針)
- C# で
Regex.IsMatch
などを包んだ CLR 関数を作成 → SQL Server に登録 →SELECT dbo.RegexIsMatch(col, pattern)
のように使用。 - 管理ポリシー上 CLR が許可できない場合は、アプリ側または SSIS/外部ETL で前処理するのが安全。
例(LIKE での代替)
-- 数字のみ(厳密には正規表現でない)
WHERE col NOT LIKE '%[^0-9]%'
すぐ使える実務スニペット
メールのTLDチェック
- MySQL:
REGEXP_LIKE(email, '@[^@]+\\.(com|jp|net)$', 'i')
- PostgreSQL:
email ~* '@[^@]+\\.(com|jp|net)$'
- Oracle:
REGEXP_LIKE(email, '@[^@]+\\.(com|jp|net)$', 'i')
日本郵便番号 123-4567
- MySQL:
REGEXP_LIKE(zip, '^[0-9]{3}-[0-9]{4}$')
- PostgreSQL:
zip ~ '^[0-9]{3}-[0-9]{4}$'
- Oracle:
REGEXP_LIKE(zip, '^[0-9]{3}-[0-9]{4}$')
- SQL Server:
zip LIKE '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]'
