プログラミングスクール デイトラ 全コース10000円OFF 8/31日まで

SQLにおけるデータ検索(基本編)

12 min 30 views
SQL基本構文

データベースを使うときに最も重要な操作のひとつが「検索」です。

検索を正しく使えるようになると、欲しいデータを自由自在に取り出せるようになります。

この記事では、SQL(Structured Query Language)の中でも、データ検索に焦点をあてて解説します

初心者の方でも理解できるように、できるだけわかりやすく表形式や例を用いて解説します。

ちなみに、どのSQLでも基本は同じです。

プログラミング言語より基本部分は簡単ですので、怖がらずに使ってみると良いでしょう。

そもそもSQLって何さ?という方はこちらの記事を先にお読みください。

SQLを使うと、以下のような検索が可能になります。

検索内容
特定の列だけ取得名前とメールアドレスだけ取り出す
条件付き取得年齢が30歳以上の人だけ取り出す
重複排除同じ部署名を一度だけ表示
並び替え成績順に昇順や降順に並べる
部分一致検索名前に「山」が含まれる人を探す
NULLチェック電話番号が未登録の人を探す
日付検索2024年1月以降に登録したデータだけ取り出す

SQLで検索するときの基本文法は SELECT文 です。

構造はとてもシンプルで、次の3つの要素から成り立っています。

役割
SELECT取り出したい列(カラム)を指定する
FROMどの表(テーブル)からデータを取得するか指定する
WHERE条件を指定する(必要に応じて省略可能)

一番簡単な検索は以下のようになります。

SELECT * FROM employees;

これは「employeesテーブルからすべての列を取得する」という意味です。
この *ワイルドカード と呼びます。

書き方意味
SELECT *全ての列を取得
SELECT name, agename列とage列だけ取得

実務では不要な列まで取るとパフォーマンスに悪影響が出るので、必要な列を指定するのが望ましいです。

射影演算とは「必要な列だけを取り出す」ことです。

SELECT name, department FROM employees;

これにより、名前と部署だけを取り出せます。
たとえば、次のような結果になります。

namedepartment
山田太郎営業
佐藤花子開発
鈴木一郎人事

SQLでは列の値を使って計算した結果を表示することもできます。

SELECT name, salary, salary * 12 AS annual_salary
FROM employees;
namesalaryannual_salary
山田太郎3000003600000
佐藤花子2800003360000

ここで使った AS は「別名をつける」キーワードです。salary * 12 という計算式に annual_salary というわかりやすい名前をつけています。

データベースには同じ値が何度も出てくることがあります。

その場合に「重複を排除」して表示するのが DISTINCT です。

SELECT DISTINCT department FROM employees;

検索の本領は 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';

結果を昇順や降順に並び替えることができます。

SELECT name, salary FROM employees
ORDER BY salary DESC;

NULLは「値が存在しない」状態を意味します。

= NULL では判定できず、必ず IS NULL を使います。


また、計算にNULLが含まれると結果もNULLになります

SQLでデータ検索を行う際の基本は次の3つです。

  1. SELECT(取得列の指定)
  2. FROM(対象のテーブル指定)
  3. WHERE(条件指定)

さらに応用として、DISTINCT・AS・比較演算子・論理演算子・LIKE・IN・BETWEEN・NULL・ORDER BY などを組み合わせることで、自由度の高い検索が可能になります。

普通に使う分には、プログラミング言語系より遥かに簡単です(ガッツリ使うと迷走する傾向にあるけど)

実際の業務では、「欲しいデータを正しく・効率よく取り出す」ことが最も重要です。

SQL検索をマスターすれば、データ分析・システム開発・レポート作成など、あらゆる場面で役立ちます。

データベース系をがっつり学びたいなら、ここが一番おススメです。

エンジニア系のスクールならどこでもSQLの科目はひっついてきます。(ただ、内容が浅いです)インターネットアカデミーさんは専門的なとこまで教えてくださいます

オススメなのですが、IT業界以外で使うなら独学でもOKです。このサイトに書いてあることぐらいしか使わないです。(今後もシリーズ化して出していくので)

こっから下は正規表現が気になった人は見てねというおまけ部分

主要構文の対応早見表

構文/機能MySQLPostgreSQLOracleSQL Server
アンカー ^ $△(LIKEなし)
量指定 {n,m}×
代替 `(ab)`
後方参照 \1×
前/後読み×
\d \w \s○(ICU)○(互換/ARE)△(基本はPOSIX推奨)×
POSIX [[:digit:]]×
大小無視フラグi / ~~* or (?i)iCOLLATE で近似
置換関数REGEXP_REPLACEregexp_replaceREGEXP_REPLACE×(CLR等)

注:DBやバージョン・照合順序で細かい差があります。移植性を優先するなら POSIX 文字クラスを使うのが無難です。

MySQL 8.0 正規表現 早見表(ICU ベース)

1) 使える演算子・関数と書式

種別シグネチャよく使う例補足
演算子expr REGEXP pat(= RLIKEname 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.cabc
* / + / ?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'
  • 全角・半角の扱い
    • \\dUnicode の数字(他言語の数字も含む)にマッチしうる点に注意。ASCII のみに限定したいなら [0-9] または [[:digit:]] を使う。
    • 英字限定は [A-Za-z]、日本語を含む「文字」は \\w だと広く取りすぎることがあります(記号も含みうる)。用途に応じて明示的なクラスを。
  • 単語境界 \\b
    日本語など空白で区切られないスクリプトでは期待どおりに働かないケースがあります。語彙境界を正確に扱う必要がある場合は明示的な前後条件(前読み/後読み)で囲う方が安全です。
  • 照合順序(照合順序 vs REGEXP)
    REGEXP_* は ICU 正規表現の評価で、データベースの照合順序 (collation) の影響を受けません。大小無視は i フラグで行います。

5) パフォーマンス最適化のコツ

  1. アンカーを付ける^ / $ を付けて走査範囲を短く。
  2. 前方一致は LIKE で前絞りWHERE col LIKE 'foo%' AND REGEXP_LIKE(col, '...')
  3. 否定の巨大クラスを避ける[^...] よりも肯定の最小集合に寄せる。
  4. 必要最小限の幅.* ではなく [^/]* のように文脈に応じて狭める
  5. 頻出パターンは生成列 + インデックス:例)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.cabc
* + ? {n} {n,} {n,m}繰り返し\\d{3,4}
^ $先頭/末尾^foo$
[...] [^...]文字クラス/否定[A-F0-9]
`(ab)`選択
(?:...)非捕捉グループ`(?: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;
created by Rinker
¥4,620 (2025/08/23 14:17:09時点 楽天市場調べ-詳細)

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/arrayregexp_split_to_table(tags, '\\s*,\\s*')
LIKE上位互換SIMILAR TO`x SIMILAR TO ‘(foo

よく使うパターン(ARE準拠)

構文意味
. * + ? {n} {n,} {n,m} / ^ $ / [...] [^...] / `(ab)/(?:…)/ 後方参照\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} ^ $ […] [^…] (ab)`
グループ:(...)、非捕捉は 未サポート(?:...)なし)
後方参照:\\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]'

関連記事