PostgreSQLの型変換を完全マスター!CAST・::演算子から暗黙的変換まで実例で学ぶ完全ガイド

データベース・SQL

「文字列を数値として計算したいのにエラーが出る…」 「日付のフォーマットを変更したいけど、やり方が分からない」 「型が合わなくてJOINできない!」

こんな悩みを抱えていませんか?

PostgreSQLでデータを扱っていると、型変換(キャスト)は避けて通れない重要なスキルです。 適切な型変換ができれば、データの加工も、複雑な集計も、すべてがスムーズになります。

この記事では、PostgreSQLの型変換について、基本的な使い方から実践的なテクニックまで、実例たっぷりで解説します。 もう「型が違います」エラーに悩まされることはありません!


スポンサーリンク

型変換の基本:なぜ必要なの?

データ型って何?

データ型とは、データの「種類」を定義するものです。 数値は数値として、文字は文字として、日付は日付として扱う必要があるんです。

主要なデータ型:

  • 数値型:INTEGER, BIGINT, NUMERIC, REAL
  • 文字型:VARCHAR, TEXT, CHAR
  • 日付型:DATE, TIMESTAMP, TIME
  • 論理型:BOOLEAN
  • その他:JSON, UUID, ARRAY

型変換が必要になる場面

よくあるケース:

  1. 文字列として保存された数値を計算したい -- '100' + '200' を 300 として計算
  2. 日付を特定のフォーマットで表示したい -- 2025-09-14 を 2025年9月14日 として表示
  3. 異なる型の列を結合(JOIN)したい -- user_id (INTEGER) と user_code (VARCHAR) を結合
  4. CSVインポート後のデータ整形 -- すべて文字列として入ったデータを適切な型に

3つの型変換方法:使い分けをマスター

方法1:CAST関数(標準SQL準拠)

最も標準的な型変換方法です。他のデータベースでも使えるので、覚えておくと便利!

基本構文:

CAST(値 AS 変換後の型)

実例:文字列を数値に変換

-- 文字列 '123' を整数に変換
SELECT CAST('123' AS INTEGER);
-- 結果: 123

-- 小数を含む文字列を数値に変換
SELECT CAST('123.45' AS NUMERIC);
-- 結果: 123.45

-- 複数の変換を組み合わせる
SELECT 
    CAST('100' AS INTEGER) + CAST('200' AS INTEGER) AS 合計;
-- 結果: 300

実例:日付の変換

-- 文字列を日付型に変換
SELECT CAST('2025-09-14' AS DATE);
-- 結果: 2025-09-14

-- タイムスタンプに変換
SELECT CAST('2025-09-14 15:30:00' AS TIMESTAMP);
-- 結果: 2025-09-14 15:30:00

方法2:::演算子(PostgreSQL独自の便利機能)

PostgreSQL特有の記法ですが、短くて書きやすいのが特徴です。

基本構文:

値::変換後の型

実例:スッキリ書ける型変換

-- CAST関数と同じ結果を、より短く書ける
SELECT '123'::INTEGER;
-- 結果: 123

-- 連続した変換もスッキリ
SELECT '123.45'::NUMERIC::INTEGER;
-- 結果: 123 (小数点以下切り捨て)

-- テーブルのカラムに対して使用
SELECT 
    user_id::TEXT,
    created_at::DATE
FROM users;

CASTと::の使い分け:

  • CAST:標準SQL準拠、可読性重視、他DBとの互換性
  • :::PostgreSQL専用、簡潔性重視、頻繁な変換時

方法3:型変換関数(特定用途に特化)

特定の変換に特化した関数群です。より細かい制御が可能!

主要な型変換関数:

-- 文字列変換
SELECT TO_CHAR(123.45, '999.99');
-- 結果: ' 123.45'

SELECT TO_CHAR(CURRENT_DATE, 'YYYY年MM月DD日');
-- 結果: '2025年09月14日'

-- 数値変換
SELECT TO_NUMBER('123,456', '999,999');
-- 結果: 123456

-- 日付変換
SELECT TO_DATE('2025/09/14', 'YYYY/MM/DD');
-- 結果: 2025-09-14

SELECT TO_TIMESTAMP('2025-09-14 15:30:00', 'YYYY-MM-DD HH24:MI:SS');
-- 結果: 2025-09-14 15:30:00

暗黙的型変換と明示的型変換

暗黙的型変換(自動変換)

PostgreSQLが自動的に型を変換してくれる場合があります。

自動変換される例:

-- 整数と小数の計算(整数が自動的に小数に変換)
SELECT 10 + 3.14;
-- 結果: 13.14

-- 文字列と数値の比較(コンテキストによる)
SELECT * FROM products WHERE price > '100';
-- '100' が自動的に数値として解釈される

注意点: 暗黙的型変換に頼りすぎると、予期しない結果になることがあります!

-- 意図しない結果の例
SELECT '10' + '20';
-- エラー: 演算子が存在しません

-- 明示的に変換すれば OK
SELECT '10'::INTEGER + '20'::INTEGER;
-- 結果: 30

明示的型変換(推奨)

常に明示的に型変換することを推奨します!

理由:

  • コードの意図が明確になる
  • エラーの原因を特定しやすい
  • パフォーマンスが向上する場合がある
-- 良い例:明示的な型変換
SELECT 
    user_id::TEXT || '_' || created_at::TEXT AS user_key
FROM users;

-- 悪い例:暗黙的な変換に依存
SELECT 
    user_id || '_' || created_at AS user_key  -- エラーの可能性
FROM users;

よく使う型変換パターン集

文字列 ⇔ 数値

文字列 → 数値:

-- 基本的な変換
SELECT '123'::INTEGER;
SELECT '123.45'::NUMERIC;
SELECT '1.23e5'::REAL;  -- 科学記数法

-- NULL や空文字の処理
SELECT 
    CASE 
        WHEN NULLIF(price_text, '') IS NULL THEN 0
        ELSE price_text::NUMERIC
    END AS price
FROM products;

-- カンマ区切りの数値
SELECT REPLACE('1,234,567', ',', '')::INTEGER;
-- 結果: 1234567

数値 → 文字列:

-- シンプルな変換
SELECT 123::TEXT;

-- フォーマット付き変換
SELECT TO_CHAR(1234567.89, 'FM999,999,999.00');
-- 結果: '1,234,567.89'

SELECT TO_CHAR(0.75, 'FM990.0%');
-- 結果: '75.0%'

日付 ⇔ 文字列

文字列 → 日付:

-- 標準フォーマット
SELECT '2025-09-14'::DATE;

-- カスタムフォーマット
SELECT TO_DATE('14/09/2025', 'DD/MM/YYYY');
SELECT TO_DATE('2025年9月14日', 'YYYY"年"MM"月"DD"日"');

-- タイムスタンプへの変換
SELECT '2025-09-14 15:30:00'::TIMESTAMP;
SELECT TO_TIMESTAMP('14-Sep-2025 3:30 PM', 'DD-Mon-YYYY HH:MI AM');

日付 → 文字列:

-- 様々なフォーマット
SELECT TO_CHAR(CURRENT_DATE, 'YYYY/MM/DD');
-- 結果: '2025/09/14'

SELECT TO_CHAR(CURRENT_DATE, 'Day, DD Month YYYY');
-- 結果: 'Saturday, 14 September 2025'

SELECT TO_CHAR(CURRENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS');
-- 結果: '2025-09-14 15:30:45'

-- 日本語フォーマット
SELECT TO_CHAR(CURRENT_DATE, 'YYYY"年"MM"月"DD"日"');
-- 結果: '2025年09月14日'

BOOLEAN ⇔ その他

文字列/数値 → BOOLEAN:

-- 文字列から
SELECT 'true'::BOOLEAN;   -- TRUE
SELECT 't'::BOOLEAN;      -- TRUE
SELECT 'yes'::BOOLEAN;    -- TRUE
SELECT '1'::BOOLEAN;      -- TRUE

SELECT 'false'::BOOLEAN;  -- FALSE
SELECT 'f'::BOOLEAN;     -- FALSE
SELECT 'no'::BOOLEAN;    -- FALSE
SELECT '0'::BOOLEAN;     -- FALSE

-- 数値から(0以外はTRUE)
SELECT (1)::BOOLEAN;      -- TRUE
SELECT (0)::BOOLEAN;      -- FALSE

BOOLEAN → 文字列/数値:

-- 文字列へ
SELECT TRUE::TEXT;        -- 'true'
SELECT FALSE::TEXT;       -- 'false'

-- 数値へ
SELECT TRUE::INTEGER;     -- 1
SELECT FALSE::INTEGER;    -- 0

-- CASE文を使った変換
SELECT 
    CASE WHEN is_active 
        THEN 'アクティブ' 
        ELSE '非アクティブ' 
    END AS status
FROM users;

JSON型との変換

通常の型 → JSON

-- 文字列をJSONに
SELECT '"Hello World"'::JSON;

-- 数値をJSONに
SELECT '123'::JSON;

-- 配列をJSONに
SELECT '[1, 2, 3]'::JSON;

-- オブジェクトをJSONに
SELECT '{"name": "田中", "age": 30}'::JSON;

-- テーブルデータをJSONに変換
SELECT row_to_json(users) FROM users;

-- 特定のカラムだけJSONに
SELECT json_build_object(
    'id', user_id,
    'name', user_name,
    'created', created_at::TEXT
) AS user_json
FROM users;

JSON → 通常の型

-- JSONから値を取り出して型変換
SELECT 
    (data->>'age')::INTEGER AS age,
    (data->>'price')::NUMERIC AS price,
    (data->>'is_active')::BOOLEAN AS is_active,
    (data->>'created_at')::TIMESTAMP AS created_at
FROM json_table;

-- JSON配列を展開
SELECT 
    value::INTEGER AS number
FROM json_array_elements('[1, 2, 3, 4, 5]'::JSON);

エラー対処法とベストプラクティス

よくあるエラーと解決法

エラー1:「invalid input syntax」

-- エラーになる例
SELECT 'abc'::INTEGER;
-- ERROR: invalid input syntax for type integer: "abc"

-- 解決法:事前チェック
SELECT 
    CASE 
        WHEN value ~ '^\d+$' THEN value::INTEGER
        ELSE NULL
    END AS safe_number
FROM data_table;

エラー2:「cannot cast type」

-- エラーになる例
SELECT ARRAY[1,2,3]::TEXT;
-- ERROR: cannot cast type integer[] to text

-- 解決法:array_to_string を使用
SELECT array_to_string(ARRAY[1,2,3], ',');
-- 結果: '1,2,3'

エラー3:日付フォーマットエラー

-- エラーになる例
SELECT '2025/14/09'::DATE;
-- ERROR: date/time field value out of range

-- 解決法:TO_DATE with format
SELECT TO_DATE('2025/14/09', 'YYYY/DD/MM');
-- 結果: 2025-09-14

安全な型変換のテクニック

1. TRY_CAST的な処理(PostgreSQL 15以降)

-- エラーを回避する変換
CREATE OR REPLACE FUNCTION safe_cast_int(text) 
RETURNS INTEGER AS $$
BEGIN
    RETURN $1::INTEGER;
EXCEPTION
    WHEN OTHERS THEN
        RETURN NULL;
END;
$$ LANGUAGE plpgsql;

-- 使用例
SELECT safe_cast_int('123');    -- 123
SELECT safe_cast_int('abc');    -- NULL

2. 正規表現での事前チェック

-- 数値チェック
SELECT 
    CASE 
        WHEN value ~ '^\d+(\.\d+)?$' THEN value::NUMERIC
        ELSE NULL
    END AS safe_number
FROM data_table;

-- 日付チェック
SELECT 
    CASE 
        WHEN date_text ~ '^\d{4}-\d{2}-\d{2}$' THEN date_text::DATE
        ELSE NULL
    END AS safe_date
FROM data_table;

3. COALESCE でデフォルト値設定

SELECT 
    COALESCE(
        NULLIF(price_text, '')::NUMERIC,
        0
    ) AS price
FROM products;

パフォーマンスを考慮した型変換

インデックスへの影響

悪い例:インデックスが使われない

-- user_id にインデックスがあっても使われない
SELECT * FROM users WHERE user_id::TEXT = '123';

-- created_at にインデックスがあっても使われない  
SELECT * FROM orders WHERE created_at::DATE = '2025-09-14';

良い例:インデックスを活用

-- 比較する値の方を変換
SELECT * FROM users WHERE user_id = '123'::INTEGER;

-- 範囲検索を使用
SELECT * FROM orders 
WHERE created_at >= '2025-09-14'::TIMESTAMP 
  AND created_at < '2025-09-15'::TIMESTAMP;

大量データの型変換

バッチ処理での型変換:

-- 新しいカラムを追加して段階的に移行
ALTER TABLE products ADD COLUMN price_numeric NUMERIC;

-- バッチで更新
UPDATE products 
SET price_numeric = price_text::NUMERIC
WHERE price_text ~ '^\d+(\.\d+)?$'
  AND price_numeric IS NULL
LIMIT 1000;

-- 確認後、カラムを入れ替え
ALTER TABLE products RENAME COLUMN price_text TO price_text_old;
ALTER TABLE products RENAME COLUMN price_numeric TO price;

実践的な使用例

CSVインポート後のデータ整形

-- CSVからインポートしたテーブル(全カラムTEXT型)
CREATE TABLE import_temp (
    user_id TEXT,
    name TEXT,
    age TEXT,
    salary TEXT,
    hire_date TEXT,
    is_active TEXT
);

-- 適切な型のテーブルに変換して挿入
INSERT INTO users (user_id, name, age, salary, hire_date, is_active)
SELECT 
    user_id::INTEGER,
    name,
    NULLIF(age, '')::INTEGER,
    REPLACE(REPLACE(salary, ',', ''), '円', '')::NUMERIC,
    TO_DATE(hire_date, 'YYYY/MM/DD'),
    CASE 
        WHEN is_active = '有効' THEN TRUE
        WHEN is_active = '無効' THEN FALSE
        ELSE NULL
    END
FROM import_temp
WHERE user_id ~ '^\d+$';  -- 数値のみ処理

レポート用のフォーマット変換

-- 売上レポート用のフォーマット済みデータ
SELECT 
    TO_CHAR(sale_date, 'YYYY年MM月') AS 年月,
    product_name AS 商品名,
    TO_CHAR(quantity, 'FM999,999') AS 販売数,
    TO_CHAR(price, 'FM999,999,999円') AS 単価,
    TO_CHAR(quantity * price, 'FM999,999,999円') AS 売上金額,
    TO_CHAR(
        (quantity * price::NUMERIC) / 
        SUM(quantity * price) OVER (PARTITION BY TO_CHAR(sale_date, 'YYYY-MM')) * 100,
        'FM990.0%'
    ) AS 売上構成比
FROM sales
ORDER BY sale_date, product_name;

まとめ:型変換マスターへの道

PostgreSQLの型変換について、重要なポイントをまとめます:

基本の3つの方法:

  1. 🔤 CAST関数 – 標準SQL準拠で可読性高い
  2. ::演算子 – PostgreSQL独自だが簡潔
  3. 📝 変換関数 – TO_CHAR、TO_DATE等で細かい制御

実践のコツ:

  • 明示的な型変換を心がける
  • エラー処理を考慮した安全な変換
  • インデックスへの影響を意識
  • 正規表現で事前チェック

よく使うパターン:

  • 文字列↔数値:計算や集計で必須
  • 日付↔文字列:レポート作成で活躍
  • JSON操作:モダンなアプリで重要

トラブル回避:

  • CASE文で条件分岐
  • COALESCEでNULL対策
  • 正規表現で妥当性チェック

型変換は地味ですが、データベース操作の基礎中の基礎です。 この記事で紹介したテクニックを使いこなせば、どんなデータ形式の課題も解決できるようになります。

エラーメッセージに怯えることなく、自信を持ってSQLを書けるようになりましょう!

コメント

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