「SQLで名前と苗字をくっつけて表示したい」
「電話番号の一部だけを取り出したい」
「データの中の文字を一括で変更したい」
データベースで作業していると、文字列を操作したい場面がよくありますよね。
そんなときに活躍するのが、SQLの文字列関数です。特にCONCAT、SUBSTRING、REPLACEは、最もよく使われる基本の文字列関数で、覚えておくととても便利です。
この記事では、これらの関数を実際のデータを使った例とともに、初心者でもわかるように詳しく説明します。
文字列関数って何?なぜ便利なの?
文字列関数とは
文字列関数は:
- 文字や文字列を操作するためのSQLの機能
- データベースに保存された文字データを加工・変換
- SELECT文の中で使って結果を整形
- WHERE文の中で条件指定にも活用
例えて言うなら: 文字データの「ハサミ」「のり」「修正ペン」のような道具
よくある使用場面
実際の業務でこんなときに使います:
データ表示の整形
- 姓と名を結合して「田中 太郎」と表示
- 郵便番号を「123-4567」の形式で表示
- 電話番号を「090-1234-5678」の形式で表示
データの抽出
- メールアドレスから「@より前の部分」だけ取得
- 商品コード「ABC-12345」から「ABC」部分のみ抽出
- 日付文字列「2024-06-24」から「2024」(年)のみ取得
データの修正・変換
- 旧システムの区分「0」を「未対応」に一括変換
- 全角英数字を半角に統一
- 不要なスペースや記号を削除
CONCAT関数:文字列をくっつける【必須】
複数の文字列を一つにまとめる関数です。
基本的な使い方
CONCAT(文字列1, 文字列2, 文字列3, ...)
特徴:
- 何個でも文字列をつなげられる
- 文字列以外(数値など)も自動で文字列に変換
- NULLがあると結果もNULLになる(データベースによる)
実際の例:基本的な文字列結合
例1:姓名の結合
テーブル:users
id | last_name | first_name |
---|---|---|
1 | 田中 | 太郎 |
2 | 佐藤 | 花子 |
3 | 鈴木 | 一郎 |
SELECT
CONCAT(last_name, ' ', first_name) AS full_name
FROM users;
結果:
full_name |
---|
田中 太郎 |
佐藤 花子 |
鈴木 一郎 |
例2:住所の結合
テーブル:addresses
prefecture | city | street |
---|---|---|
東京都 | 新宿区 | 西新宿1-1-1 |
大阪府 | 大阪市 | 梅田2-2-2 |
SELECT
CONCAT(prefecture, city, street) AS full_address
FROM addresses;
結果:
full_address |
---|
東京都新宿区西新宿1-1-1 |
大阪府大阪市梅田2-2-2 |
例3:商品情報の結合
SELECT
CONCAT('商品名:', product_name, '(価格:', price, '円)') AS product_info
FROM products;
結果例:
商品名:ノートパソコン(価格:98000円)
商品名:マウス(価格:2500円)
高度な使い方
改行を含む文字列の作成
SELECT
CONCAT(
'氏名:', last_name, ' ', first_name, CHAR(10),
'電話:', phone, CHAR(10),
'メール:', email
) AS contact_info
FROM customers;
CHAR(10) は改行コードです。
条件付きの文字列結合
SELECT
CONCAT(
product_name,
CASE
WHEN discount_rate > 0
THEN CONCAT('(', discount_rate, '%OFF)')
ELSE ''
END
) AS display_name
FROM products;
データベース別の注意点
MySQL
-- MySQL では CONCAT が標準
SELECT CONCAT('Hello', ' ', 'World');
PostgreSQL
-- PostgreSQL でも CONCAT 使用可能
SELECT CONCAT('Hello', ' ', 'World');
-- || 演算子も使える
SELECT 'Hello' || ' ' || 'World';
SQL Server
-- SQL Server では + 演算子が一般的
SELECT 'Hello' + ' ' + 'World';
-- CONCAT も使える(2012以降)
SELECT CONCAT('Hello', ' ', 'World');
Oracle
-- Oracle では || 演算子が標準
SELECT 'Hello' || ' ' || 'World' FROM dual;
SUBSTRING関数:文字列の一部を取り出す【便利】
文字列の指定した部分だけを抜き出す関数です。
基本的な使い方
書き方が2種類あります:
-- 標準SQL形式
SUBSTRING(文字列 FROM 開始位置 FOR 長さ)
-- 一般的な形式
SUBSTRING(文字列, 開始位置, 長さ)
重要なポイント:
- 開始位置は1から始まる(0ではない)
- 長さを省略すると最後まで取得
- 範囲を超えてもエラーにならない
実際の例:文字列の抽出
例1:商品コードの分解
テーブル:products
product_code | product_name |
---|---|
ABC-12345 | ノートPC |
DEF-67890 | マウス |
GHI-11111 | キーボード |
-- カテゴリ部分(最初の3文字)を抽出
SELECT
product_code,
SUBSTRING(product_code, 1, 3) AS category,
SUBSTRING(product_code, 5, 5) AS item_number
FROM products;
結果:
product_code | category | item_number |
---|---|---|
ABC-12345 | ABC | 12345 |
DEF-67890 | DEF | 67890 |
GHI-11111 | GHI | 11111 |
例2:日付文字列の分解
テーブル:orders
order_date |
---|
2024-06-24 |
2024-12-31 |
2023-01-15 |
SELECT
order_date,
SUBSTRING(order_date, 1, 4) AS year,
SUBSTRING(order_date, 6, 2) AS month,
SUBSTRING(order_date, 9, 2) AS day
FROM orders;
結果:
order_date | year | month | day |
---|---|---|---|
2024-06-24 | 2024 | 06 | 24 |
2024-12-31 | 2024 | 12 | 31 |
2023-01-15 | 2023 | 01 | 15 |
例3:メールアドレスの分解
SELECT
email,
SUBSTRING(email, 1, POSITION('@' IN email) - 1) AS username,
SUBSTRING(email, POSITION('@' IN email) + 1) AS domain
FROM users;
例: taro@example.com
→ taro
と example.com
に分離
応用例:実際の業務での活用
郵便番号の整形
-- 「1234567」を「123-4567」に変換
SELECT
CONCAT(
SUBSTRING(postal_code, 1, 3),
'-',
SUBSTRING(postal_code, 4, 4)
) AS formatted_postal
FROM addresses;
電話番号の整形
-- 「09012345678」を「090-1234-5678」に変換
SELECT
CONCAT(
SUBSTRING(phone, 1, 3), '-',
SUBSTRING(phone, 4, 4), '-',
SUBSTRING(phone, 8, 4)
) AS formatted_phone
FROM contacts;
文字数制限での表示
-- 商品名が長い場合は20文字で切って「...」を付ける
SELECT
CASE
WHEN LENGTH(product_name) > 20
THEN CONCAT(SUBSTRING(product_name, 1, 20), '...')
ELSE product_name
END AS display_name
FROM products;
データベース別の書き方
MySQL
-- 両方の書き方が使える
SELECT SUBSTRING('Hello World', 7, 5); -- 'World'
SELECT SUBSTRING('Hello World' FROM 7 FOR 5); -- 'World'
-- 右から文字を取る場合
SELECT RIGHT('Hello World', 5); -- 'World'
PostgreSQL
-- FROM FOR 形式が標準
SELECT SUBSTRING('Hello World' FROM 7 FOR 5); -- 'World'
-- 正規表現での抽出も可能
SELECT SUBSTRING('ABC-12345' FROM '[A-Z]+'); -- 'ABC'
SQL Server
-- 第2引数が開始位置、第3引数が長さ
SELECT SUBSTRING('Hello World', 7, 5); -- 'World'
-- 右から取る場合
SELECT RIGHT('Hello World', 5); -- 'World'
REPLACE関数:文字列を置き換える【実用的】
文字列の中の特定の部分を別の文字列に置き換える関数です。
基本的な使い方
REPLACE(元の文字列, 置換対象, 置換後)
特徴:
- 一致するすべての部分を置換
- 大文字小文字の区別はデータベースによる
- 置換対象が見つからない場合は元の文字列をそのまま返す
実際の例:文字列の置換
例1:日付形式の変換
テーブル:events
event_date |
---|
2024-06-24 |
2024-12-31 |
2023-01-15 |
-- ハイフンをスラッシュに変換
SELECT
event_date,
REPLACE(event_date, '-', '/') AS formatted_date
FROM events;
結果:
event_date | formatted_date |
---|---|
2024-06-24 | 2024/06/24 |
2024-12-31 | 2024/12/31 |
2023-01-15 | 2023/01/15 |
例2:不要な文字の削除
-- スペースを削除
SELECT
REPLACE(customer_name, ' ', '') AS name_no_space
FROM customers;
-- 複数の文字を連続で置換
SELECT
REPLACE(REPLACE(phone_number, '-', ''), ' ', '') AS clean_phone
FROM contacts;
例3:データの正規化
テーブル:products
status |
---|
在庫あり |
在庫なし |
在庫少 |
-- 日本語を英語コードに変換
SELECT
status,
REPLACE(
REPLACE(
REPLACE(status, '在庫あり', 'IN_STOCK'),
'在庫なし', 'OUT_OF_STOCK'
),
'在庫少', 'LOW_STOCK'
) AS status_code
FROM products;
結果:
status | status_code |
---|---|
在庫あり | IN_STOCK |
在庫なし | OUT_OF_STOCK |
在庫少 | LOW_STOCK |
高度な使い方
HTMLタグの除去
-- HTMLタグを削除してテキストのみ抽出
SELECT
REPLACE(
REPLACE(
REPLACE(description, '<p>', ''),
'</p>', ''
),
'<br>', '\n'
) AS plain_text
FROM articles;
複数置換のパターン
-- 複数の不要文字を一度に処理
SELECT
REPLACE(
REPLACE(
REPLACE(
REPLACE(input_text, ' ', ' '), -- 全角スペース→半角
'(', '('
), -- 全角括弧→半角
')', ')'
),
'!', '!'
) AS normalized_text
FROM user_inputs;
条件付き置換
-- 特定の条件下でのみ置換
SELECT
product_name,
CASE
WHEN category = '食品'
THEN REPLACE(product_name, '無添加', '★無添加★')
ELSE product_name
END AS display_name
FROM products;
関数の組み合わせ技【応用】
複数の文字列関数を組み合わせると、さらに強力な処理ができます。
よくある組み合わせパターン
パターン1:抽出 → 結合
-- 電話番号を「090」「1234」「5678」に分けて整形
SELECT
CONCAT(
SUBSTRING(phone, 1, 3), '-',
SUBSTRING(phone, 4, 4), '-',
SUBSTRING(phone, 8, 4)
) AS formatted_phone
FROM contacts;
パターン2:置換 → 抽出
-- 「2024年06月24日」から「2024-06-24」を作成
SELECT
CONCAT(
SUBSTRING(REPLACE(REPLACE(date_text, '年', '-'), '月', '-'), 1, 10),
SUBSTRING(date_text, -2, 2)
) AS iso_date
FROM date_table;
パターン3:結合 → 置換
-- 名前を結合してから敬称を付ける
SELECT
REPLACE(
CONCAT(last_name, ' ', first_name),
' ',
' 様 '
) AS formal_name
FROM customers;
実践的な応用例
CSVデータの処理
-- CSVの1行から各項目を抽出
WITH csv_data AS (
SELECT '田中,太郎,30,東京' as csv_line
)
SELECT
SUBSTRING(csv_line, 1, POSITION(',' IN csv_line) - 1) AS last_name,
-- 以下、カンマの位置を計算して各項目を抽出
FROM csv_data;
URLからドメイン抽出
SELECT
SUBSTRING(
url,
POSITION('//' IN url) + 2,
POSITION('/', SUBSTRING(url, POSITION('//' IN url) + 2)) - 1
) AS domain
FROM website_urls;
日本語住所の正規化
-- 住所から都道府県、市区町村、番地を分離
SELECT
address,
CASE
WHEN address LIKE '%都%' THEN SUBSTRING(address, 1, POSITION('都' IN address))
WHEN address LIKE '%府%' THEN SUBSTRING(address, 1, POSITION('府' IN address))
WHEN address LIKE '%県%' THEN SUBSTRING(address, 1, POSITION('県' IN address))
END AS prefecture
FROM addresses;
パフォーマンスとベストプラクティス
パフォーマンスの考慮点
インデックスの活用
Good:
-- インデックスが効く
SELECT * FROM users WHERE last_name = '田中';
Bad:
-- インデックスが効かない
SELECT * FROM users WHERE SUBSTRING(last_name, 1, 1) = '田';
関数の使用回数を最小化
Good:
-- 一度だけ計算して変数に保存
WITH processed AS (
SELECT
id,
UPPER(TRIM(name)) AS clean_name
FROM users
)
SELECT * FROM processed WHERE clean_name LIKE 'A%';
Bad:
-- 毎回計算
SELECT * FROM users
WHERE UPPER(TRIM(name)) LIKE 'A%'
OR UPPER(TRIM(name)) LIKE 'B%';
コードの可読性向上
複雑な処理はCTEで分割
WITH step1 AS (
-- 第1段階:基本的な置換
SELECT
id,
REPLACE(REPLACE(raw_data, ' ', ' '), '\n', '') AS cleaned_data
FROM raw_table
),
step2 AS (
-- 第2段階:項目の抽出
SELECT
id,
SUBSTRING(cleaned_data, 1, 50) AS summary,
SUBSTRING(cleaned_data, 51) AS details
FROM step1
)
SELECT * FROM step2;
コメントの活用
SELECT
product_code,
-- 商品カテゴリ(最初の3文字)
SUBSTRING(product_code, 1, 3) AS category,
-- 商品番号(4文字目以降の数字部分)
SUBSTRING(product_code, 5) AS item_number
FROM products;
データベース別の違いと対応
主要データベースの比較表
機能 | MySQL | PostgreSQL | SQL Server | Oracle |
---|---|---|---|---|
CONCAT | ✓ | ✓ | ✓ (2012+) | × |
演算子 | × | ✓ | ||
+ 演算子 | × | × | ✓ | × |
SUBSTRING | ✓ | ✓ | ✓ | ✓ |
REPLACE | ✓ | ✓ | ✓ | ✓ |
移植性を考慮したSQL
-- どのDBでも動く文字列結合
SELECT
CASE
WHEN last_name IS NOT NULL AND first_name IS NOT NULL
THEN last_name || ' ' || first_name
ELSE COALESCE(last_name, first_name, '')
END AS full_name
FROM users;
よくあるエラーと対処法
エラーパターンと解決方法
エラー1:NULLによる予期しない結果
問題:
SELECT CONCAT(last_name, ' ', first_name) FROM users;
-- first_nameがNULLの場合、結果全体がNULLになる
解決:
SELECT CONCAT(
COALESCE(last_name, ''),
' ',
COALESCE(first_name, '')
) FROM users;
エラー2:文字列の境界を超えたアクセス
問題:
SELECT SUBSTRING('ABC', 1, 10); -- 長さが足りない
対処:
-- 事前に長さをチェック
SELECT
CASE
WHEN LENGTH(text_column) >= 10
THEN SUBSTRING(text_column, 1, 10)
ELSE text_column
END
FROM table_name;
エラー3:データ型の不一致
問題:
SELECT CONCAT('Price: ', price) FROM products;
-- price が数値型の場合エラーになることがある
解決:
SELECT CONCAT('Price: ', CAST(price AS VARCHAR)) FROM products;
実際のプロジェクトでの活用例
ケース1:ECサイトの商品管理
-- 商品情報の表示用フォーマット作成
SELECT
product_id,
CONCAT(
category_name, ' - ', product_name,
CASE
WHEN sale_price < regular_price
THEN CONCAT(' (',
ROUND((regular_price - sale_price) / regular_price * 100),
'% OFF)')
ELSE ''
END
) AS display_title,
CONCAT('¥', FORMAT(sale_price, 0)) AS price_display
FROM products p
JOIN categories c ON p.category_id = c.category_id;
ケース2:顧客管理システムのデータ移行
-- 旧システムのデータを新形式に変換
SELECT
customer_id,
-- 氏名の正規化
TRIM(REPLACE(REPLACE(customer_name, ' ', ' '), ' ', ' ')) AS normalized_name,
-- 電話番号の正規化(ハイフンなしの11桁に統一)
REPLACE(REPLACE(REPLACE(phone, '-', ''), ' ', ''), '(', '') AS clean_phone,
-- 住所の都道府県抽出
CASE
WHEN address LIKE '%都%' THEN SUBSTRING(address, 1, POSITION('都' IN address))
WHEN address LIKE '%府%' THEN SUBSTRING(address, 1, POSITION('府' IN address))
WHEN address LIKE '%県%' THEN SUBSTRING(address, 1, POSITION('県' IN address))
ELSE '不明'
END AS prefecture
FROM old_customers;
ケース3:ログ解析システム
-- ログファイルからURL情報を抽出
SELECT
log_date,
-- URLからドメインを抽出
SUBSTRING(
request_url,
POSITION('//' IN request_url) + 2,
POSITION('/', SUBSTRING(request_url, POSITION('//' IN request_url) + 2)) - 1
) AS domain,
-- ユーザーエージェントからブラウザ情報を抽出
CASE
WHEN user_agent LIKE '%Chrome%' THEN 'Chrome'
WHEN user_agent LIKE '%Firefox%' THEN 'Firefox'
WHEN user_agent LIKE '%Safari%' THEN 'Safari'
ELSE 'Other'
END AS browser
FROM access_logs;
よくある質問
基本的な疑問
Q: 文字列関数の実行順序は?
A: 内側から外側に向かって実行されます。例:UPPER(TRIM(name))
はTRIM→UPPERの順。
Q: パフォーマンスが心配…
A: WHERE句での使用は注意。可能な限りインデックスが効く条件を併用しましょう。
Q: NULLの扱いが分からない
A: COALESCEやISNULLを使ってNULL対策をしっかり行いましょう。
トラブル対応
Q: 文字化けが起きる
A: データベースとクライアントの文字エンコーディングを確認してください。
Q: 期待した結果にならない
A: 関数の引数(開始位置、長さ)を再確認し、実際のデータで動作確認しましょう。
応用
Q: 正規表現は使える?
A: PostgreSQLやMySQLでは使えますが、標準SQLではありません。
Q: より高度な文字列処理は?
A: TRIM、UPPER、LOWER、LENGTH、POSITIONなども組み合わせると強力です。
まとめ
SQL文字列関数をマスターして、データ処理のプロになろう!
今日覚えるべき重要ポイント
必須の3関数:
- CONCAT – 文字列の結合に必須
- SUBSTRING – 部分文字列の抽出に便利
- REPLACE – 文字列の置換で データクレンジング
学習ステップ
初心者レベル(今すぐできる):
- 基本的なCONCATで名前結合
- SUBSTRINGで日付分解
- REPLACEで簡単な置換
中級レベル(1週間後の目標):
- 複数関数の組み合わせ
- NULL対策の実装
- 実際のデータでの練習
上級レベル(1ヶ月後の目標):
- 複雑なデータ変換
- パフォーマンス最適化
- エラーハンドリング
ポイント
- 小さい例から始める
- 実際のデータで練習
- エラーを恐れずに試行錯誤
- 結果を必ず確認
コメント