PostgreSQL: PostgreSQL組み込み関数:詳細ガイド
最終更新:2026-08-26
1. 学習内容
- 数学関数:
ABS / ROUND / CEIL / FLOOR / MOD / POWER / SQRT - 文字列関数:
LENGTH / CONCAT / LOWER / UPPER / TRIM / SUBSTRING / REPLACE / SPLIT_PART / LEFT / RIGHT - 日付関数:
NOW / CURRENT_DATE / AGE / EXTRACT / DATE_TRUNC / TO_CHAR / TO_DATE - 条件関数:
COALESCE / NULLIF / GREATEST / LEAST - シーケンス関数:
NEXTVAL / CURRVAL / SETVAL - 関数を組み合わせるテクニック
2. ストーリー
AliceはEコマースのデータアナリストで、月末に月次売上レポートを作成する必要があります。レポートでは以下が求められます:
order_dateを2025-June-30形式に整形- 顧客名+注文番号をレポート行のタイトルに連結
- NULL処理:欠損している割引値をデフォルトの0に
- 金額計算:税込総額と割引後の正味額を正確に計算
- シーケンスの使用:レポート行に連番を生成
AliceはPostgreSQLの組み込み関数を組み合わせてレポートを作成する必要があります。
3. 概念:数学関数
(1) 基本的な数学関数
| 関数 | 意味 | 例 | 結果 |
|---|---|---|---|
ABS(x) |
絶対値 | ABS(-15) |
15 |
ROUND(x, n) |
n桁に四捨五入 | ROUND(3.1415, 2) |
3.14 |
CEIL(x) |
切り上げ | CEIL(3.2) |
4 |
FLOOR(x) |
切り捨て | FLOOR(3.8) |
3 |
MOD(x, y) |
剰余 | MOD(10, 3) |
1 |
POWER(x, y) |
べき乗 | POWER(2, 10) |
1024 |
SQRT(x) |
平方根 | SQRT(144) |
12 |
SIGN(x) |
符号 | SIGN(-5) |
-1 |
TRUNC(x, n) |
切り捨て(小数部) | TRUNC(3.1415, 2) |
3.14 |
▶ サンプル:ROUNDで固定小数桁に丸める
SELECT
unit_price,
ROUND(unit_price, 0) AS rounded_0,
ROUND(unit_price, 2) AS rounded_2
FROM products
WHERE category = 'Electronics';
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
▶ サンプル:CEIL / FLOORによる丸め
SELECT
total_amount,
CEIL(total_amount) AS ceil_value,
FLOOR(total_amount) AS floor_value,
total_amount - FLOOR(total_amount) AS decimal_part
FROM orders
WHERE order_status = 'completed';
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) ROUND vs TRUNC vs CEIL/FLOOR
| 関数 | 動作 | 3.5 の結果 |
3.2 の結果 |
-3.5 の結果 |
|---|---|---|---|---|
ROUND(x) |
最も近い整数に丸める | 4 | 3 | -4 |
TRUNC(x) |
小数部を切り捨て | 3 | 3 | -3 |
CEIL(x) |
切り上げ | 4 | 4 | -3 |
FLOOR(x) |
切り捨て | 3 | 3 | -4 |
▶ サンプル:MOD剰余
SELECT
order_id,
total_amount,
MOD(order_id, 10) AS partition_key,
total_amount * POWER(1.08, 1) AS with_annual_tax
FROM orders
WHERE order_status = 'completed';
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
▶ サンプル:SQRTとPOWERの組み合わせ
SELECT
unit_price,
SQRT(unit_price) AS price_sqrt,
POWER(unit_price, 1.0 / 3) AS price_cbrt
FROM products
WHERE unit_price > 0
ORDER BY unit_price DESC;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
4. 概念:文字列関数
(1) 大文字小文字と長さ
| 関数 | 意味 | 例 | 結果 |
|---|---|---|---|
LOWER(s) |
小文字に変換 | LOWER('Hello') |
hello |
UPPER(s) |
大文字に変換 | UPPER('Hello') |
HELLO |
INITCAP(s) |
先頭文字を大文字化 | INITCAP('hello world') |
Hello World |
LENGTH(s) |
文字数 | LENGTH('PostgreSQL') |
10 |
BIT_LENGTH(s) |
ビット長 | BIT_LENGTH('A') |
8 |
CHAR_LENGTH(s) |
LENGTHと同じ | CHAR_LENGTH('ABC') |
3 |
▶ サンプル:大文字小文字変換
SELECT
product_name,
LOWER(product_name) AS lower_name,
UPPER(product_name) AS upper_name,
INITCAP(LOWER(product_name)) AS title_name
FROM products
WHERE is_active = true;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) 連結と部分文字列
| 関数 | 意味 | 例 | 結果 |
|---|---|---|---|
CONCAT(s1, s2, ...) |
連結(NULL安全) | CONCAT('A', NULL, 'B') |
AB |
CONCAT_WS(sep, s1, s2) |
区切り文字付き連結 | CONCAT_WS('-', 'A','B') |
A-B |
| `s1 | s2` | 連結(NULL安全でない) | |
SUBSTRING(s FROM n FOR len) |
部分文字列 | SUBSTRING('Hello' FROM 2 FOR 3) |
ell |
LEFT(s, n) |
左からn文字 | LEFT('Hello', 3) |
Hel |
RIGHT(s, n) |
右からn文字 | RIGHT('Hello', 2) |
lo |
▶ サンプル:CONCAT vs ||
SELECT
product_name,
CONCAT(product_name, ' - $', unit_price) AS safe_concat,
product_name || ' - $' || unit_price AS unsafe_concat,
CONCAT_WS(' | ', product_name, category, unit_price::TEXT) AS ws_concat
FROM products;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) 検索と置換
| 関数 | 意味 | 例 | 結果 |
|---|---|---|---|
POSITION(sub IN s) |
位置を検索 | POSITION('sql' IN 'postgresql') |
8 |
STRPOS(s, sub) |
同上(引数が逆) | STRPOS('postgresql', 'sql') |
8 |
REPLACE(s, from, to) |
置換 | REPLACE('ABC', 'B', 'X') |
AXC |
OVERLAY(s PLACING t FROM n) |
上書き | OVERLAY('XXXX' PLACING 'AB' FROM 2) |
XABX |
SPLIT_PART(s, delim, n) |
区切り文字で分割しn番目を取得 | SPLIT_PART('A-B-C', '-', 2) |
B |
TRIM(s) |
前後の空白を除去 | TRIM(' Hi ') |
Hi |
LTRIM(s) |
左の空白を除去 | LTRIM(' Hi ') |
Hi |
RTRIM(s) |
右の空白を除去 | RTRIM(' Hi ') |
Hi |
RPAD(s, len, fill) |
右パディング | RPAD('Hi', 5, 'x') |
Hixxx |
LPAD(s, len, fill) |
左パディング | LPAD('42', 5, '0') |
00042 |
REVERSE(s) |
逆順 | REVERSE('ABC') |
CBA |
▶ サンプル:SPLIT_PARTでCSVフィールドを解析
SELECT
product_name,
SPLIT_PART(category_path, '/', 1) AS top_category,
SPLIT_PART(category_path, '/', 2) AS sub_category
FROM products
WHERE category_path LIKE '%/%';
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
▶ サンプル:REPLACEとTRIMの組み合わせ
SELECT
TRIM(product_name) AS clean_name,
REPLACE(product_name, ' ', ' ') AS no_double_spaces
FROM products
WHERE is_active = true;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
▶ サンプル:LPADで数値を整形
SELECT
LPAD(product_id::TEXT, 8, '0') AS formatted_id,
product_name,
unit_price
FROM products
ORDER BY product_id;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
5. 概念:日付関数
(1) 現在時刻の取得
| 関数 | 戻り値の型 | 説明 |
|---|---|---|
CURRENT_DATE |
DATE | 現在の日付 |
CURRENT_TIME |
TIME WITH TZ | 現在の時刻(タイムゾーン付き) |
CURRENT_TIMESTAMP |
TIMESTAMP WITH TZ | 現在の日付と時刻 |
NOW() |
TIMESTAMP WITH TZ | CURRENT_TIMESTAMPと同じ |
LOCALTIMESTAMP |
TIMESTAMP | 現在の時刻(タイムゾーンなし) |
CLOCK_TIMESTAMP() |
TIMESTAMP WITH TZ | リアルタイムクロック(呼び出しごとに変化) |
▶ サンプル:現在時刻の取得
SELECT
CURRENT_DATE AS today,
CURRENT_TIMESTAMP AS now_ts,
NOW() AS now_func,
LOCALTIMESTAMP AS local_ts;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) 日付演算
| 式 | 意味 | 結果の例 |
|---|---|---|
DATE '2025-01-15' + 7 |
日数を加算 | 2025-01-22 |
DATE '2025-01-15' - DATE '2025-01-01' |
日数の差 | 14 |
NOW() + INTERVAL '30 days' |
インターバルを加算 | 30日後 |
NOW() - INTERVAL '1 year' |
インターバルを減算 | 1年前 |
AGE(end, start) |
インターバル記述 | 1 year 2 mons 3 days |
AGE(timestamp) |
現在からのインターバル | — |
▶ サンプル:日付演算
SELECT
order_date,
order_date + 7 AS due_date,
CURRENT_DATE - order_date AS days_ago,
AGE(CURRENT_DATE, order_date) AS age_since_order
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '90 days';
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) EXTRACTとDATE_TRUNC
| 使用法 | 意味 | 例 | 結果 |
|---|---|---|---|
EXTRACT(YEAR FROM ts) |
年を抽出 | EXTRACT(YEAR FROM NOW()) |
2025 |
EXTRACT(MONTH FROM ts) |
月を抽出 | EXTRACT(MONTH FROM NOW()) |
6 |
EXTRACT(DAY FROM ts) |
日を抽出 | — | — |
EXTRACT(DOW FROM ts) |
曜日(0=日曜) | — | 1(月曜) |
EXTRACT(QUARTER FROM ts) |
四半期 | — | 2 |
EXTRACT(EPOCH FROM ts) |
Unixタイムスタンプ | — | 1719500400 |
DATE_TRUNC('month', ts) |
月初に切り捨て | DATE_TRUNC('month', '2025-06-15') |
2025-06-01 |
DATE_TRUNC('year', ts) |
年初に切り捨て | — | 2025-01-01 |
▶ サンプル:EXTRACTで日付部分を取得
SELECT
order_date,
EXTRACT(YEAR FROM order_date) AS order_year,
EXTRACT(MONTH FROM order_date) AS order_month,
EXTRACT(QUARTER FROM order_date) AS order_quarter,
EXTRACT(DOW FROM order_date) AS weekday
FROM orders
WHERE order_status = 'completed';
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
▶ サンプル:DATE_TRUNCで月次集計
SELECT
DATE_TRUNC('month', order_date) AS month_start,
COUNT(*) AS order_count,
SUM(total_amount) AS monthly_revenue
FROM orders
WHERE order_status = 'completed'
AND order_date >= DATE_TRUNC('year', CURRENT_DATE)
GROUP BY DATE_TRUNC('month', order_date)
ORDER BY month_start;
Output:
count
-------
5
(1 row)
(4) TO_CHARとTO_DATE
| 関数 | 目的 | 例 |
|---|---|---|
TO_CHAR(ts, fmt) |
時刻→文字列 | TO_CHAR(NOW(), 'YYYY-Month-DD') |
TO_DATE(s, fmt) |
文字列→日付 | TO_DATE('2025-06-15', 'YYYY-MM-DD') |
TO_TIMESTAMP(s, fmt) |
文字列→タイムスタンプ | TO_TIMESTAMP('2025-06-15 14:30', 'YYYY-MM-DD HH24:MI') |
一般的な書式テンプレート:
| テンプレート | 意味 | 出力例 |
|---|---|---|
YYYY |
4桁の年 | 2025 |
YY |
2桁の年 | 25 |
Month |
完全な月名 | June |
MON |
省略月名 | Jun |
MM |
2桁の月 | 06 |
DD |
2桁の日 | 15 |
HH24 |
24時間制 | 14 |
MI |
分 | 30 |
SS |
秒 | 45 |
▶ サンプル:TO_CHAR日付整形
SELECT
order_id,
TO_CHAR(order_date, 'YYYY-Month-DD') AS formatted_date,
TO_CHAR(order_date, 'YYYY-MM-DD HH24:MI:SS') AS full_datetime,
TO_CHAR(total_amount, 'FM$999,999.00') AS formatted_amount
FROM orders
WHERE order_status = 'completed';
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
▶ サンプル:TO_DATEで文字列を日付に変換
SELECT
TO_DATE('2025-06-15', 'YYYY-MM-DD') AS parsed_date,
TO_DATE('15/06/2025', 'DD/MM/YYYY') AS eu_date,
TO_DATE('Jun 15 2025', 'Mon DD YYYY') AS us_date;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
6. 概念:条件関数
(1) COALESCE
COALESCE(val1, val2, ...) は最初のNULLでない引数を返し、最も一般��なNULL処理関数です。
| 式 | 結果 |
|---|---|
COALESCE(NULL, NULL, 'default') |
default |
COALESCE(100, 0) |
100 |
COALESCE(NULL, 0) |
0 |
▶ サンプル:COALESCEで欠損割引を処理
SELECT
product_name,
unit_price,
discount_rate,
COALESCE(discount_rate, 0) AS safe_discount,
unit_price * (1 - COALESCE(discount_rate, 0)) AS net_price
FROM products;
Output:
count
-------
5
(1 row)
(2) NULLIF
NULLIF(val1, val2) は2つの値が等しい場合にNULLを返し、そうでなければval1を返します。ゼロ除算エラーを防ぐためによく使われます。
| 式 | 結果 |
|---|---|
NULLIF(100, 100) |
NULL |
NULLIF(100, 0) |
100 |
100 / NULLIF(0, 0) |
NULL(エラーの代わりに) |
▶ サンプル:NULLIFでゼロ除算を防止
SELECT
product_name,
unit_price,
total_sold,
unit_price / NULLIF(total_sold, 0) AS revenue_per_unit
FROM products
WHERE is_active = true;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(3) GREATEST / LEAST
| 関数 | 意味 | 例 | 結果 |
|---|---|---|---|
GREATEST(a, b, c) |
最大値を返す | GREATEST(10, 20, 5) |
20 |
LEAST(a, b, c) |
最小値を返す | LEAST(10, 20, 5) |
5 |
▶ サンプル:GREATEST / LEAST
SELECT
product_name,
unit_price,
COALESCE(discount_rate, 0) AS discount,
unit_price * (1 - GREATEST(COALESCE(discount_rate, 0), 0.5)) AS capped_net_price,
LEAST(unit_price, 999.99) AS price_cap
FROM products
WHERE is_active = true;
Output:
count
-------
5
(1 row)
(4) 条件関数の比較
| 関数 | シナリオ | NULL処理 |
|---|---|---|
COALESCE(a, b) |
デフォルト値を提供 | 最初のNULLでない値を返す |
NULLIF(a, b) |
値が等しい場合にNULL | NULLを処理しない |
GREATEST(a, b) |
最大値を取得 | いずれかがNULLならNULLを返す |
LEAST(a, b) |
最小値を取得 | いずれかがNULLならNULLを返す |
7. 概念:シーケンス関数
(1) シーケンス操作関数
| 関数 | 意味 | 例 |
|---|---|---|
NEXTVAL('seq_name') |
インクリメントして次の値を返す | NEXTVAL('order_id_seq') → 1001 |
CURRVAL('seq_name') |
現在のセッションの最後のNEXTVALを返す | CURRVAL('order_id_seq') → 1001 |
SETVAL('seq_name', n) |
シーケンスの現在値を設定 | SETVAL('order_id_seq', 5000) |
SETVAL('seq_name', n, false) |
値を設定、次のNEXTVALはnを返す | SETVAL('order_id_seq', 5000, false) |
▶ サンプル:NEXTVALで番号を生成
INSERT INTO orders (order_id, customer_id, total_amount, order_date)
VALUES (NEXTVAL('order_id_seq'), 1001, 299.99, CURRENT_DATE);
Output:
INSERT 0 1
▶ サンプル:CURRVALで現在値を表示
SELECT
NEXTVAL('report_seq') AS report_number,
CURRVAL('report_seq') AS current_seq;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
(2) SETVALでシーケンスをリセット
| 呼び出し | 次のNEXTVALが返す値 |
|---|---|
SETVAL('seq', 100) |
101 |
SETVAL('seq', 100, false) |
100 |
SETVAL('seq', 1, false) |
1(先頭にリセット) |
▶ サンプル:SETVALでシーケンスをリセット
-- シーケンスを1から再開するようにリセット
SELECT SETVAL('order_id_seq', 1, false);
-- シーケンスを特定の高い値に設定(例:データ移行後)
SELECT SETVAL('order_id_seq', (SELECT MAX(order_id) FROM orders));
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
8. フローチャート:関数の選択
flowchart TD
A[必要な操作は?] --> B{データ型は?}
B -->|数値| C{何が必要?}
B -->|文字列| D{何が必要?}
B -->|日付| E{何が必要?}
B -->|NULL処理| F[COALESCE / NULLIF]
C -->|丸め| G[ROUND / TRUNC]
C -->|整数に丸め| H[CEIL / FLOOR]
C -->|計算| I[ABS / MOD / POWER / SQRT]
D -->|連結| J[CONCAT / ||]
D -->|部分文字列| K[SUBSTRING / LEFT / RIGHT]
D -->|置換| L[REPLACE / SPLIT_PART]
D -->|大文字小文字| M[LOWER / UPPER / INITCAP]
D -->|トリム| N[TRIM / LTRIM / RTRIM]
E -->|現在時刻| O[NOW / CURRENT_TIMESTAMP]
E -->|部分抽出| P[EXTRACT]
E -->|切り捨て| Q[DATE_TRUNC]
E -->|整形| R[TO_CHAR]
E -->|解析| S[TO_DATE / TO_TIMESTAMP]
style A fill:#e1f5fe
style F fill:#c8e6c9
9. 総合的な例
Aliceは完全な月次売上レポートを��成しました。
-- ステップ1: 日付整形付き月次収益レポート
SELECT
TO_CHAR(DATE_TRUNC('month', order_date), 'YYYY-Month') AS report_month,
COUNT(*) AS order_count,
SUM(total_amount)::NUMERIC(12,2) AS gross_revenue,
SUM(total_amount * 0.08)::NUMERIC(12,2) AS tax_collected,
SUM(total_amount * 1.08)::NUMERIC(12,2) AS total_with_tax
FROM orders
WHERE order_status = 'completed'
AND order_date >= DATE_TRUNC('year', CURRENT_DATE)
GROUP BY DATE_TRUNC('month', order_date)
ORDER BY report_month;
-- ステップ2: 文字列整形とNULL処理付き商品サマリー
SELECT
LPAD(p.product_id::TEXT, 6, '0') AS product_code,
INITCAP(LOWER(p.product_name)) AS display_name,
CONCAT('$', ROUND(p.unit_price, 2)) AS price_label,
COALESCE(p.discount_rate, 0) AS discount,
ROUND(p.unit_price * (1 - COALESCE(p.discount_rate, 0)), 2) AS net_price,
LEAST(ROUND(p.unit_price * (1 - COALESCE(p.discount_rate, 0)), 2), 999.99) AS capped_price
FROM products p
WHERE p.is_active = true
AND p.unit_price > NULLIF(p.cost_price, 0)
ORDER BY net_price DESC;
-- ステップ3: 年数計算付き顧客注文サマリー
SELECT
CONCAT(c.first_name, ' ', c.last_name) AS customer_name,
COUNT(o.order_id) AS total_orders,
SUM(o.total_amount)::NUMERIC(12,2) AS lifetime_value,
AGE(MAX(o.order_date), MIN(o.order_date)) AS customer_span,
EXTRACT(YEAR FROM AGE(CURRENT_DATE, c.register_date)) AS years_since_join
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id
WHERE o.order_status = 'completed'
GROUP BY c.customer_id, c.first_name, c.last_name, c.register_date
ORDER BY lifetime_value DESC
FETCH FIRST 25 ROWS ONLY;
❓ よくある質問
|| の違いは?|| はいずれかのオペランドがNULLの場合、結果全体がNULLになります。NULL安全な連結にはCONCATまたはCONCAT_WSを使用してください。DATE1 - DATE2 は整数の日数を返し、AGE(DATE1, DATE2) は年、月、日を含むINTERVALを返します(例:1 year 2 mons 3 days)— より人間に優しいですが、減算より精度は低くなります。📖 まとめ
- 数学関数:
ROUND/CEIL/FLOORで丸め、ABS/MOD/POWER/SQRTで計算。整数除算と精度に注意 - 文字列関数:
CONCAT(NULL安全)/||(NULL安全でない)、SUBSTRING/LEFT/RIGHTで分割、REPLACE/SPLIT_PARTで置換/分割 - 日付関数:
NOW/CURRENT_TIMESTAMPで現在時刻、EXTRACTで部分抽出、DATE_TRUNCで切り捨て、TO_CHAR/TO_DATEで整形と解析 - 条件関数:
COALESCEは最初のNULLでない値を返し、NULLIFは等しい場合にNULLを返してゼロ除算を防ぎ、GREATEST/LEASTは極値を取得 - シーケンス関数:
NEXTVALはインクリメントして返し、CURRVALは現在値を読み取り、SETVALはリセット - 関数の組み合わせは実践的な中核スキルです — ネストと型キャストをうまく活用してください
📝 練習問題
-
⭐
productsからproduct_nameとunit_priceを選択し、CONCATで価格ラベル($99.99形式)を作成し、ROUNDで小数点以下2桁に丸め、価格の降順でソートするクエリを書いてください。 -
⭐⭐ 2025年の月次売上レポートを生成するクエリを書いてください:
DATE_TRUNC('month', order_date)で月ごとにグループ化し、TO_CHARでYYYY-MM形式に整形し、月次注文数と合計金額(NUMERIC(12,2))を計算し、COALESCEでNULLの可能性がある割引を処理してください。 -
⭐⭐⭐ 顧客ごとのレポート行を生成するクエリを書いてください:
CONCAT_WSで名前を結合し、AGEで顧客の登録期間を計算し、EXTRACTで登録年を抽出し、GREATESTで割引を50%にキャップし、NULLIFで平均注文額計算時のゼロ除算を防ぎ、LPADで顧客IDを6桁の番号に整形し、最後にNEXTVAL('report_seq')でレポート行のシーケンスを生成してください。