【Oracle】SUBSTR()関数で文字列の一部を切り出す方法|負の値・SUBSTRB・実務パターンも解説

【Oracle】SUBSTR()関数で文字列の一部を切り出す方法 Oracle

OracleデータベースでSQLを書いていると、「文字列の一部だけを取り出したい」という場面は頻繁にあります。

例えば、電話番号から市外局番だけを抽出したり、ファイルパスからファイル名を取り出したり、郵便番号のハイフン前後を分割したりする処理です。

こうした文字列の部分切り出しに使うのが SUBSTR() 関数です。

この記事では、SUBSTR()の基本構文から、負の開始位置で末尾から切り出す方法バイト単位で切り出すSUBSTRB()INSTR()と組み合わせた動的な切り出しREGEXP_SUBSTR()との比較実務で使える応用パターンまで、網羅的に解説します。

スポンサーリンク

SUBSTR()関数の基本構文

SUBSTR() は、文字列の指定した位置から指定した文字数分を切り出す関数です。

構文
SUBSTR(文字列, 開始位置 [, 文字数])
引数 説明 必須
文字列 切り出し元の文字列(カラム名やリテラル) 必須
開始位置 切り出しを開始する文字位置(1始まり) 必須
文字数 切り出す文字数(省略時は末尾まで) 任意

ポイント:Oracleの文字位置は 1始まり です。プログラミング言語のように0始まりではないので注意してください。ただし、0を指定しても1と同じ動作をします。

SUBSTR()の基本的な使い方

開始位置と文字数を指定して切り出す

最も基本的な使い方です。文字列の途中から指定した文字数分を取り出します。

SQL
-- 3文字目から3文字分を切り出す
SELECT SUBSTR('Hello World', 3, 3) AS result FROM dual;

実行結果

RESULT
------
llo

「H(1) e(2) l(3) l(4) o(5) …」のように数え、3文字目の l から3文字分 llo が取得されます。

先頭から指定文字数を切り出す

開始位置を 1 にすれば、先頭から切り出せます。

SQL
-- 先頭から5文字を取得
SELECT SUBSTR('Oracle Database', 1, 5) AS result FROM dual;

実行結果

RESULT
------
Oracl

文字数を省略した場合(末尾まで取得)

第3引数(文字数)を省略すると、開始位置から末尾まですべて取得されます。

SQL
-- 8文字目から末尾まで取得
SELECT SUBSTR('Oracle Database', 8) AS result FROM dual;

実行結果

RESULT
--------
Database

この書き方は、「n文字目以降をすべて取得」したい場合に便利です。

負の開始位置で末尾から切り出す

開始位置に負の値を指定すると、文字列の末尾から数えた位置から切り出しが始まります。

SQL
-- 末尾から5文字目を起点に3文字分を切り出す
SELECT SUBSTR('Hello World', -5, 3) AS result FROM dual;

-- 末尾から3文字を取得(文字数省略)
SELECT SUBSTR('Hello World', -3) AS result FROM dual;

実行結果

-- 1つ目のクエリ
RESULT
------
Wor

-- 2つ目のクエリ
RESULT
------
rld

-5 を指定した場合、末尾から5文字目(W)を起点として切り出します。文字数を省略すると末尾まで取得されるため、-3 で「末尾3文字」を取得できます。

開始位置の数え方まとめ

  • 正の値:先頭から数える(1 = 先頭文字)
  • 負の値:末尾から数える(-1 = 最後の文字)
  • 0:1と同じ扱い(先頭文字から)

負の開始位置 + 文字数のパターン一覧

'ABCDEFGHIJ'(10文字)を例に、負の開始位置の動作を確認しましょう。

SQL
SELECT
  SUBSTR('ABCDEFGHIJ', -1)     AS last_1,      -- J
  SUBSTR('ABCDEFGHIJ', -3)     AS last_3,      -- HIJ
  SUBSTR('ABCDEFGHIJ', -5, 2) AS mid_2,       -- FG
  SUBSTR('ABCDEFGHIJ', -7, 4) AS mid_4        -- DEFG
FROM dual;

実行結果

LAST_1  LAST_3  MID_2  MID_4
------  ------  -----  -----
J       HIJ     FG     DEFG

開始位置に0を指定した場合の動作

Oracleでは開始位置に 0 を指定した場合、1と同じ扱いになります。

SQL
-- 開始位置0 と 開始位置1 の比較
SELECT
  SUBSTR('ABCDE', 0, 3) AS pos_0,   -- ABC
  SUBSTR('ABCDE', 1, 3) AS pos_1    -- ABC
FROM dual;

実行結果

POS_0  POS_1
-----  -----
ABC    ABC

注意:Oracle では開始位置 0 は 1 と同じ動作ですが、他のRDBMSでは異なる場合があります。可読性のためにも、先頭から切り出す場合は 1 を明示することを推奨します。

テーブルのカラムに対して使う

実務では文字列リテラルではなく、テーブルのカラムに対してSUBSTR()を使うことがほとんどです。

SQL
-- 従業員名の先頭2文字をイニシャルとして取得
SELECT
  emp_name,
  SUBSTR(emp_name, 1, 2) AS initial
FROM employees;

実行結果

EMP_NAME    INITIAL
----------  -------
田中太郎    田中
鈴木花子    鈴木
Smith       Sm

SUBSTRB() – バイト単位で切り出す

SUBSTRB() は、文字数ではなくバイト数を基準に切り出す関数です。

構文
SUBSTRB(文字列, 開始バイト位置 [, バイト数])

SUBSTR と SUBSTRB の違い

マルチバイト文字(日本語など)を扱う場合に、SUBSTR と SUBSTRB の差が出ます。

SQL(AL32UTF8環境の場合)
-- 文字単位(SUBSTR)
SELECT SUBSTR('あいうえお', 1, 3) AS char_result FROM dual;

-- バイト単位(SUBSTRB)
SELECT SUBSTRB('あいうえお', 1, 3) AS byte_result FROM dual;

実行結果

-- SUBSTR(文字単位)
CHAR_RESULT
-----------
あいう

-- SUBSTRB(バイト単位)
BYTE_RESULT
-----------
あ

UTF-8 では日本語1文字が3バイトなので、SUBSTRB で3バイトを指定すると1文字分しか取得できません。

関数 単位 マルチバイト文字 主な用途
SUBSTR() 文字単位 1文字 = 1 通常の文字列操作
SUBSTRB() バイト単位 1文字 = 2〜4バイト 固定長レコードの処理

注意:SUBSTRBでマルチバイト文字の途中のバイトを指定すると、文字化けや空白が返される場合があります。バイト境界を意識して指定してください。

SUBSTRB の実務的な使用例

外部システム連携で固定長レコードを扱う場合など、バイト単位での切り出しが必要になることがあります。

SQL
-- 固定長レコード(100バイト)から各フィールドを抽出
SELECT
  SUBSTRB(record_data, 1,  10) AS customer_code,   -- 1〜10バイト
  SUBSTRB(record_data, 11, 30) AS customer_name,   -- 11〜40バイト
  SUBSTRB(record_data, 41, 15) AS phone_number     -- 41〜55バイト
FROM import_records;

WHERE句での活用

SUBSTR() は WHERE 句でも使えます。先頭文字列で条件を絞り込む場合に活用できます。

前方一致的な使い方

SQL
-- 社員番号の先頭2文字が「AB」の社員を取得
SELECT emp_id, emp_name
FROM employees
WHERE SUBSTR(emp_id, 1, 2) = 'AB';

注意:WHERE句でSUBSTR()を使うとインデックスが効きません(関数インデックスを作成している場合を除く)。前方一致であれば LIKE 'AB%' のほうがパフォーマンスが良い場合が多いです。

末尾の文字列で絞り込む

SQL
-- ファイル名の末尾4文字が「.csv」のレコードを取得
SELECT file_name
FROM file_list
WHERE SUBSTR(file_name, -4) = '.csv';

末尾の文字列で絞り込む場合は、負の開始位置が非常に便利です。LIKE '%.csv' と同等ですが、SUBSTR のほうが文字数を厳密に指定できます。

INSTR()と組み合わせた動的な切り出し

INSTR() 関数で特定の文字の位置を取得し、それをSUBSTR()の引数に使うことで、動的な位置で文字列を切り出せます。

基本パターン:区切り文字の前後を取得

SQL
-- メールアドレスからユーザー名とドメインを分離
SELECT
  email,
  SUBSTR(email, 1, INSTR(email, '@') - 1) AS user_name,
  SUBSTR(email, INSTR(email, '@') + 1)    AS domain
FROM employees;

実行結果

EMAIL                 USER_NAME    DOMAIN
--------------------  -----------  -----------
tanaka@example.com    tanaka       example.com
suzuki@company.co.jp  suzuki       company.co.jp

ポイント:INSTR(email, '@')@ の位置を返します。その位置の前後でSUBSTRの範囲を調整することで、区切り文字を含まない形で分割できます。

ファイルパスからファイル名を取得

SQL
-- ファイルパスから最後の「/」以降(ファイル名)を取得
SELECT
  file_path,
  SUBSTR(file_path, INSTR(file_path, '/', -1) + 1) AS file_name
FROM file_list;

実行結果

FILE_PATH                    FILE_NAME
---------------------------  ----------
/home/user/docs/report.pdf   report.pdf
/var/log/app/error.log       error.log

INSTR(file_path, '/', -1) は末尾から検索して最後の / の位置を返すため、ファイル名だけを正確に取得できます。

ファイル名から拡張子を取得

SQL
-- ファイル名から拡張子を取得
SELECT
  file_name,
  SUBSTR(file_name, INSTR(file_name, '.', -1) + 1) AS extension
FROM file_list;

実行結果

FILE_NAME      EXTENSION
-------------  ---------
report.pdf     pdf
data.csv       csv
image.jpeg     jpeg

実務で使える応用パターン

1. 電話番号の分解

ハイフン区切りの電話番号を市外局番・市内局番・加入者番号に分割します。

SQL
-- 電話番号「03-1234-5678」を分解
SELECT
  phone,
  SUBSTR(phone, 1, INSTR(phone, '-') - 1)  AS area_code,
  SUBSTR(phone,
    INSTR(phone, '-') + 1,
    INSTR(phone, '-', 1, 2) - INSTR(phone, '-') - 1
  ) AS local_code,
  SUBSTR(phone, INSTR(phone, '-', 1, 2) + 1) AS subscriber
FROM customers;

実行結果

PHONE         AREA_CODE  LOCAL_CODE  SUBSCRIBER
------------  ---------  ----------  ----------
03-1234-5678  03         1234        5678
06-9876-5432  06         9876        5432

INSTR(phone, '-', 1, 2) は「1文字目から検索して2番目に見つかった-の位置」を返します。これにより、2つ目の区切り文字以降を正確に取得できます。

2. 郵便番号の分割

SQL
-- 郵便番号「123-4567」を前半3桁・後半4桁に分割
SELECT
  postal_code,
  SUBSTR(postal_code, 1, 3) AS area,
  SUBSTR(postal_code, 5, 4) AS detail
FROM addresses
WHERE postal_code LIKE '___-____';

実行結果

POSTAL_CODE  AREA  DETAIL
-----------  ----  ------
100-0001     100   0001
530-0001     530   0001

3. 年月日の分解(文字列形式)

SQL
-- 「20260305」形式の日付文字列を分解
SELECT
  date_str,
  SUBSTR(date_str, 1, 4) AS yyyy,
  SUBSTR(date_str, 5, 2) AS mm,
  SUBSTR(date_str, 7, 2) AS dd
FROM (
  SELECT '20260305' AS date_str FROM dual
);

実行結果

DATE_STR  YYYY  MM  DD
--------  ----  --  --
20260305  2026  03  05

4. 文字列のマスク処理

個人情報の一部をマスクする場合にも SUBSTR() が活用できます。

SQL
-- 名前の先頭1文字だけ表示し、残りを「***」でマスク
SELECT
  emp_name,
  SUBSTR(emp_name, 1, 1) || '***' AS masked_name
FROM employees;

-- クレジットカード番号の末尾4桁のみ表示
SELECT
  '****-****-****-' || SUBSTR(card_number, -4) AS masked_card
FROM payments;

実行結果

EMP_NAME  MASKED_NAME
--------  -----------
田中太郎  田***
鈴木花子  鈴***

MASKED_CARD
-------------------
****-****-****-1234
****-****-****-5678

REGEXP_SUBSTR()との比較

Oracle 10g以降では、正規表現で文字列を切り出す REGEXP_SUBSTR() が使えます。SUBSTR() との使い分けを見てみましょう。

比較項目 SUBSTR() REGEXP_SUBSTR()
切り出し基準 位置と文字数 正規表現パターン
パフォーマンス 高速 正規表現の評価分やや遅い
柔軟性 固定位置・固定長向け パターンマッチ向け
対応バージョン 全バージョン Oracle 10g以降
可読性 シンプル 正規表現の知識が必要

同じ処理をSUBSTR と REGEXP_SUBSTR で比較

SQL
-- 「ABC-123-XYZ」から数字部分を取得

-- SUBSTR + INSTR の場合
SELECT SUBSTR('ABC-123-XYZ',
  INSTR('ABC-123-XYZ', '-') + 1,
  INSTR('ABC-123-XYZ', '-', 1, 2) - INSTR('ABC-123-XYZ', '-') - 1
) AS result1 FROM dual;

-- REGEXP_SUBSTR の場合
SELECT REGEXP_SUBSTR('ABC-123-XYZ', '\d+') AS result2 FROM dual;

実行結果(両方同じ)

RESULT
------
123

使い分けの指針

  • 位置や長さが決まっているSUBSTR() を使う(シンプル・高速)
  • パターンで抽出したいREGEXP_SUBSTR() を使う(柔軟)
  • パフォーマンスが重要 → 可能な限り SUBSTR() を選択

他のRDBMSとの比較

文字列切り出し関数はRDBMSによって名前や構文が異なります。

RDBMS 関数名 構文例 開始位置0の扱い
Oracle SUBSTR() SUBSTR(str, 1, 3) 0は1と同じ
MySQL SUBSTRING() / SUBSTR() SUBSTRING(str, 1, 3) 空文字を返す
PostgreSQL SUBSTRING() / SUBSTR() SUBSTRING(str FROM 1 FOR 3) 0は1と同じ
SQL Server SUBSTRING() SUBSTRING(str, 1, 3) 0は1と同じ

主な違い

機能 Oracle MySQL PostgreSQL SQL Server
負の開始位置 対応 対応 非対応 非対応
文字数省略 末尾まで 末尾まで 末尾まで 省略不可(必須)
バイト単位関数 SUBSTRB() なし SUBSTRING(... USING OCTETS) なし
各RDBMSのコード例
各RDBMS比較
-- Oracle
SELECT SUBSTR('Hello World', 1, 5) FROM dual;          -- Hello
SELECT SUBSTR('Hello World', -5)    FROM dual;          -- World

-- MySQL
SELECT SUBSTRING('Hello World', 1, 5);                   -- Hello
SELECT SUBSTRING('Hello World', -5);                      -- World

-- PostgreSQL
SELECT SUBSTRING('Hello World' FROM 1 FOR 5);            -- Hello
SELECT SUBSTR('Hello World', 1, 5);                     -- Hello

-- SQL Server
SELECT SUBSTRING('Hello World', 1, 5);                   -- Hello
-- ※ SQL ServerではSUBSTR()は使えません

よくあるエラーと対処法

エラー / 問題 原因 対処法
ORA-00909 引数の数が不正 引数は2つまたは3つ必要
ORA-01722 開始位置や文字数に数値以外を指定 数値を指定する(文字列の場合はTO_NUMBERで変換)
NULLが返る 元の文字列がNULL NVL()でNULLを事前に置換
空文字が返る 開始位置が文字列長を超えている LENGTH()で文字列長を事前に確認
文字化け(SUBSTRB) マルチバイト文字の途中で切り出し 文字境界を考慮した位置指定、またはSUBSTR()を使用

NULLの場合の対処

SUBSTR() に NULL を渡すと結果も NULL になります。NVL() で事前にデフォルト値を設定しておくと安全です。

SQL
-- NULLの場合の動作
SELECT SUBSTR(NULL, 1, 3) AS result FROM dual;
-- 結果: NULL

-- NVLで安全に処理
SELECT SUBSTR(NVL(emp_name, '(未設定)'), 1, 5) AS result
FROM employees;

文字列長を超えた場合の動作

SQL
-- 開始位置が文字列長を超える場合
SELECT SUBSTR('ABC', 10, 3) AS result FROM dual;
-- 結果: NULL

-- 文字数が残りの文字列長より大きい場合
SELECT SUBSTR('ABC', 2, 100) AS result FROM dual;
-- 結果: BC(エラーにならず、残りすべてが返る)

ポイント:文字数に大きな値を指定してもエラーにはなりません。残りの文字数分だけ返されるので、「開始位置以降すべて取得」する場合に大きな値を指定するテクニックも使えます。ただし、可読性を考えると文字数を省略するほうが望ましいです。

パフォーマンスの注意点

1. WHERE句でのインデックス無効化

WHERE SUBSTR(column, ...) のように関数を使うと、そのカラムのインデックスは使われません。

SQL
-- インデックスが効かない(フルスキャンになる可能性)
SELECT * FROM employees
WHERE SUBSTR(emp_code, 1, 3) = 'ABC';

-- 代替案1: LIKEを使う(インデックスが効く)
SELECT * FROM employees
WHERE emp_code LIKE 'ABC%';

-- 代替案2: 関数インデックスを作成
CREATE INDEX idx_emp_code_prefix
ON employees (SUBSTR(emp_code, 1, 3));

関数インデックスについて

  • WHERE句で SUBSTR() を使う検索が頻繁にある場合は、関数インデックス(Function-Based Index)を検討しましょう
  • ただし、インデックスの維持コスト(INSERT/UPDATE時のオーバーヘッド)も考慮してください
  • 前方一致の検索であれば LIKE 'prefix%' のほうがシンプルで効率的です

2. SELECT句での大量レコード処理

SELECT句でSUBSTR()を使うのは通常問題ありませんが、大量レコードに対して複雑な文字列操作を繰り返す場合は、処理時間に注意が必要です。

SQL
-- 非効率: 同じカラムに対してSUBSTRを何度も呼ぶ
SELECT
  SUBSTR(data, 1, 3)   AS part1,
  SUBSTR(data, 4, 5)   AS part2,
  SUBSTR(data, 9, 2)   AS part3,
  SUBSTR(data, 11, 4)  AS part4,
  SUBSTR(data, 15)     AS part5
FROM large_table; -- 数百万件

-- ヒント: 多数のSUBSTR呼び出しは、
-- PL/SQL処理やアプリケーション側での分割も検討

他の文字列関数との組み合わせ

SUBSTR() は他の文字列関数と組み合わせることで、より高度な文字列操作が可能です。

関数の組み合わせ 用途
SUBSTR + INSTR 区切り文字の位置をもとに動的に切り出す
SUBSTR + LENGTH 文字列長に応じた切り出し
SUBSTR + TRIM 切り出し後の空白を除去
SUBSTR + UPPER / LOWER 切り出した文字の大文字/小文字変換
SUBSTR + REPLACE 切り出しと文字置換の組み合わせ
SUBSTR + TO_NUMBER 切り出した数値文字列を数値に変換

SUBSTR + LENGTH:先頭と末尾を除いた中身を取得

SQL
-- 括弧を除いた中身を取得 「(内容)」→「内容」
SELECT SUBSTR('(Oracle)', 2, LENGTH('(Oracle)') - 2) AS result
FROM dual;

実行結果

RESULT
------
Oracle

SUBSTR + UPPER:先頭文字を大文字にする

SQL
-- 先頭文字を大文字に変換(INITCAP相当の手動実装)
SELECT
  UPPER(SUBSTR('oracle database', 1, 1)) ||
  SUBSTR('oracle database', 2) AS result
FROM dual;

実行結果

RESULT
---------------
Oracle database

まとめ

項目 内容
基本構文 SUBSTR(文字列, 開始位置 [, 文字数])
開始位置 正の値 = 先頭から、負の値 = 末尾から、0 = 1と同じ
文字数省略 開始位置から末尾までを取得
バイト単位 SUBSTRB() を使用(マルチバイト文字に注意)
動的切り出し INSTR() と組み合わせて区切り位置を動的に取得
パフォーマンス WHERE句での使用はインデックス無効化に注意
NULL対策 NVL() で事前にデフォルト値を設定

SUBSTR() はOracleの文字列操作において最も基本的かつ重要な関数です。INSTR() との組み合わせで動的な切り出しができるようになれば、電話番号・メールアドレス・ファイルパスなど、実務で必要な文字列分割のほとんどに対応できます。

まずは基本構文をしっかり覚え、次に負の開始位置やINSTR()との組み合わせパターンを身につけていきましょう。