SQL文字列関数完全ガイド―CONCAT、SUBSTRING、REPLACEの使い方と実例

データベース・SQL

「SQLで名前と苗字をくっつけて表示したい」
「電話番号の一部だけを取り出したい」
「データの中の文字を一括で変更したい」

データベースで作業していると、文字列を操作したい場面がよくありますよね。

そんなときに活躍するのが、SQLの文字列関数です。特にCONCATSUBSTRINGREPLACEは、最もよく使われる基本の文字列関数で、覚えておくととても便利です。

この記事では、これらの関数を実際のデータを使った例とともに、初心者でもわかるように詳しく説明します。

スポンサーリンク

文字列関数って何?なぜ便利なの?

文字列関数とは

文字列関数は:

  • 文字や文字列を操作するための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

idlast_namefirst_name
1田中太郎
2佐藤花子
3鈴木一郎
SELECT 
    CONCAT(last_name, ' ', first_name) AS full_name
FROM users;

結果:

full_name
田中 太郎
佐藤 花子
鈴木 一郎

例2:住所の結合

テーブル:addresses

prefecturecitystreet
東京都新宿区西新宿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_codeproduct_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_codecategoryitem_number
ABC-12345ABC12345
DEF-67890DEF67890
GHI-11111GHI11111

例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_dateyearmonthday
2024-06-2420240624
2024-12-3120241231
2023-01-1520230115

例3:メールアドレスの分解

SELECT 
    email,
    SUBSTRING(email, 1, POSITION('@' IN email) - 1) AS username,
    SUBSTRING(email, POSITION('@' IN email) + 1) AS domain
FROM users;

例: taro@example.comtaroexample.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_dateformatted_date
2024-06-242024/06/24
2024-12-312024/12/31
2023-01-152023/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;

結果:

statusstatus_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;

データベース別の違いと対応

主要データベースの比較表

機能MySQLPostgreSQLSQL ServerOracle
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関数:

  1. CONCAT – 文字列の結合に必須
  2. SUBSTRING – 部分文字列の抽出に便利
  3. REPLACE – 文字列の置換で データクレンジング

学習ステップ

初心者レベル(今すぐできる):

  1. 基本的なCONCATで名前結合
  2. SUBSTRINGで日付分解
  3. REPLACEで簡単な置換

中級レベル(1週間後の目標):

  1. 複数関数の組み合わせ
  2. NULL対策の実装
  3. 実際のデータでの練習

上級レベル(1ヶ月後の目標):

  1. 複雑なデータ変換
  2. パフォーマンス最適化
  3. エラーハンドリング

ポイント

  • 小さい例から始める
  • 実際のデータで練習
  • エラーを恐れずに試行錯誤
  • 結果を必ず確認

コメント

タイトルとURLをコピーしました