SUBSTRING は、指定した位置から指定した文字数分だけ文字列の一部を返します。テキスト列からプレフィックスやコード、メールのドメインを取り出すときに SQL で使う方法です。例は PostgreSQL、MySQL、SQLite、SQL Server で動作します。
SUBSTRING の使い方
SUBSTRING には、文字列、開始位置(最初の文字は 1)、取り出す文字数を渡します。長さを省略すると末尾までをすべて取り出します:
SUBSTRING(string, start, length)
SELECT SUBSTRING('Joe Bloggs', 1, 3);
-- Output: Joe
-- No length: the rest of the string (SQL Server needs all three arguments)
SELECT SUBSTRING('Joe Bloggs', 5);
-- Output: Bloggs
実際のクエリでは、文字列は通常カラムです。ORD-2024-001 のような注文番号では、5〜8文字目に年が入っています:
-- Input: orders
-- order_number
-- ------------
-- ORD-2024-001
-- ORD-2025-014
-- ORD-2026-102
SELECT order_number, SUBSTRING(order_number, 5, 4) AS year
FROM orders;
-- Output:
-- order_number | year
-- -------------+-----
-- ORD-2024-001 | 2024
-- ORD-2025-014 | 2025
-- ORD-2026-102 | 2026
知っておくべきルール
- 位置は 0 ではなく 1 から始まります。負の開始位置は文字列の末尾から数えますが、これは MySQL と SQLite のみです:
SELECT SUBSTRING('Joe Bloggs', -6, 6);
-- Output: Bloggs
-- PostgreSQL and SQL Server return '' (an empty string) instead
- 末尾を超えてもエラーにはなりません。存在する文字だけが返され、開始位置が末尾を超えている場合は空文字列になります:
SELECT SUBSTRING('Bob', 1, 10);
-- Output: Bob
SELECT SUBSTRING('Hello', 10, 5);
-- Output: '' (an empty string)
- NULL を渡すと NULL が返ります。値が必要な場合は COALESCE で囲みます:
SELECT SUBSTRING(NULL, 1, 5);
-- Output: NULL
-- SQL Server rejects a bare NULL here, but a NULL column gives NULL
-
プレフィックスでの絞り込みには SUBSTRING ではなく LIKE を使います。
WHERE SUBSTRING(product_code, 1, 3) = 'ELE'はWHERE product_code LIKE 'ELE%'と同じ行を返しますが、product_codeのインデックスを使えるのは LIKE の方だけです。
別の構文
SUBSTRING の別名である SUBSTR や、文字列の両端から文字を取り出す LEFT と RIGHT もよく見かけます:
-- Not in SQL Server
SELECT SUBSTR('Joe Bloggs', 1, 3);
-- Output: Joe
-- Not in SQLite: use SUBSTRING('Joe Bloggs', 1, 3) and SUBSTRING('Joe Bloggs', -6)
SELECT LEFT('Joe Bloggs', 3);
-- Output: Joe
SELECT RIGHT('Joe Bloggs', 6);
-- Output: Bloggs
-- PostgreSQL and MySQL: the standard SQL form
SELECT SUBSTRING('Joe Bloggs' FROM 1 FOR 3);
-- Output: Joe
特定の文字の後から始めるには、まずその位置を求めます:
SELECT INSTR('joe@example.com', '@');
-- Output: 4
-- PostgreSQL: POSITION('@' IN 'joe@example.com'); SQL Server: CHARINDEX('@', 'joe@example.com')
SUBSTRING はいつ使うべき?
必要な部分が文字列内の決まった位置、または検索できる文字の直後にある場合は SUBSTRING を使いましょう。ほとんどの用途は次の4つのいずれかに当てはまります。
先頭の数文字を取り出す
コードや名前は先頭に意味を持つことがよくあります。商品コードのカテゴリ、イニシャル、長い説明文の短いプレビューなどです。SUBSTRING(column, 1, n) で先頭の n 文字を取り出せます。
固定フォーマットの値からフィールドを取り出す
すべての値が同じレイアウトなら、各部分はどの行でも同じ位置にあります。(303) 555-0147 のような電話番号の市外局番、ORD-2024-001 の年、古いシステムの固定長レコードの各フィールドなどです。
区切り文字の後のテキストを抽出する
メールのドメイン、ファイルの拡張子、URL のパスは、行ごとに位置が変わる文字の後から始まります。INSTR でその位置を求め、1文字後から文字列の残りを取り出します。
末尾の数文字を残す
カード番号や口座番号のマスク、サフィックスの読み取りでは、末尾から数える必要があります。文字列の長さから開始位置を計算するか、MySQL と SQLite では負の開始位置を使います。
SUBSTRING の実例
コード内のカテゴリ別に商品数を数える
商品コードは3文字のカテゴリで始まります。そのプレフィックスでグループ化して、各カテゴリの商品数を数えます:
-- Input: products
-- product_code | name
-- -------------+-----------
-- ELE-1234 | USB-C Hub
-- FUR-0087 | Desk Chair
-- ELE-5521 | Webcam
-- OFF-2210 | Stapler
SELECT SUBSTRING(product_code, 1, 3) AS category, COUNT(*) AS products
FROM products
GROUP BY SUBSTRING(product_code, 1, 3)
ORDER BY category;
-- Output:
-- category | products
-- ---------+---------
-- ELE | 2
-- FUR | 1
-- OFF | 1
固定長レコードを解析する
古いシステムからのエクスポートでは、区切り文字なしでフィールドが固定位置に詰め込まれていることがよくあります。ここでは名前が8文字(スペースで埋められており、TRIM で除去します)、生年月日が次の8文字、都市コードが最後の3文字です:
-- Input: legacy_data
-- record
-- -------------------
-- JOHNDOE 19850315NYC
-- MARYLEE 19780912CHI
-- JANEROE 19900722LAX
SELECT
TRIM(SUBSTRING(record, 1, 8)) AS name,
SUBSTRING(record, 9, 8) AS birth_date,
SUBSTRING(record, 17, 3) AS city_code
FROM legacy_data;
-- Output:
-- name | birth_date | city_code
-- --------+------------+----------
-- JOHNDOE | 19850315 | NYC
-- MARYLEE | 19780912 | CHI
-- JANEROE | 19900722 | LAX
各ファイルの拡張子を取得する
ドットを見つけて、その後ろをすべて取り出します。WHERE 句でドットのないファイルを除外します:
-- Input: documents
-- filename
-- ---------------
-- q3-report.pdf
-- budget.xlsx
-- README
-- team-photo.jpeg
SELECT
filename,
SUBSTRING(filename, INSTR(filename, '.') + 1) AS extension
FROM documents
WHERE INSTR(filename, '.') > 0;
-- Output:
-- filename | extension
-- ----------------+----------
-- q3-report.pdf | pdf
-- budget.xlsx | xlsx
-- team-photo.jpeg | jpeg
PostgreSQL では INSTR の代わりに POSITION('.' IN filename) を使います。SQL Server では CHARINDEX('.', filename) を使い、LEN(filename) などの長さを指定します。
カード番号をマスクする
カード番号はすべて16桁とは限らないため、開始位置を固定すると American Express の番号が途中で切れてしまいます。代わりに末尾から数えます:
-- Input: payments
-- customer_name | card_number
-- --------------+-----------------
-- Lena Fischer | 4111111111111111
-- Marcus Reid | 5500005555555559
-- Aiko Tanaka | 378282246310005
-- Wrong: position 13 only works for 16-digit cards
SELECT customer_name, '**** ' || SUBSTRING(card_number, 13, 4) AS masked_card
FROM payments;
-- Output:
-- customer_name | masked_card
-- --------------+------------
-- Lena Fischer | **** 1111
-- Marcus Reid | **** 5559
-- Aiko Tanaka | **** 005
-- Right: start 4 characters from the end
SELECT customer_name, '**** ' || SUBSTRING(card_number, LENGTH(card_number) - 3, 4) AS masked_card
FROM payments;
-- Output:
-- customer_name | masked_card
-- --------------+------------
-- Lena Fischer | **** 1111
-- Marcus Reid | **** 5559
-- Aiko Tanaka | **** 0005
MySQL と SQL Server では、|| の代わりに CONCAT() で文字列を連結します。SQL Server ではさらに LENGTH() の代わりに LEN() を使います。
SUBSTRING についてさらに読む
このブログの記事:
- SQL LIKE ガイド: パターンで始まる、終わる、または含むテキストにマッチさせる
- SQL COALESCE ガイド: SUBSTRING がそのまま返す NULL を置き換える
- SQL GROUP BY ガイド: 抽出した値で行をグループ化して数える
- SQL 日付フォーマットガイド: 文字列を切り出さずに日付をフォーマットし、日付の要素を取り出す
公式ドキュメント:
Beekeeper Studioは無料のオープンソースのデータベースGUIです
これまで使った中で最高のSQLクエリ&エディタツールです。データベースの管理に必要なものがすべて揃っています。 - ⭐⭐⭐⭐⭐ Mit
Beekeeper Studioは高速で直感的、そして使いやすいツールです。多数のデータベースに対応し、Windows、Mac、Linuxで快適に動作します。
Beekeeper Studio 利用者の声
「Beekeeper Studio は以前の SQL ワークフローを完全に置き換えてくれました。高速で直感的で、データベース作業がまた楽しくなりました。」
「多くのデータベース GUI を試してきましたが、Beekeeper は機能とシンプルさのバランスが完璧です。とにかくちゃんと動きます。」