◀ 6.

Oracle SQL関数まとめ|集計・文字列・日時・NULL・分析関数

Oracle Database 19cのSQL関数を、やりたい処理から選ぶためのページです。集計で行をまとめる場合と、分析関数で各行を残す場合を区別し、文字数・バイト数、日時の基準、NULLの扱いも確認します。MySQLなどとの比較例は対象製品を分けて掲載しています。

SQL全体の項目はSQL、製品の入口はOracleから確認できます。

掲載例は一次資料に基づく学習用で、Oracleでの実行確認は行っていません。実際の表定義・型・権限・NLS・クライアントを確認し、定義変更例は独立した学習環境で検討してください。

Oracle SQL関数:COUNTのNULL条件、文字数とバイト数、集計と分析の違い

この記事の要点

目的Oracle 19cで選ぶもの確認する点
件数・合計・平均COUNT / SUM / AVG / MIN / MAXNULLと空集合、GROUP BY
文字列の加工CONCAT / SUBSTR / UPPER / LOWER / TRIMCONCATは2引数。3項以上なら入れ子または連結演算子
長さLENGTH / LENGTHB文字数とバイト数を分ける
日時SYSDATE / CURRENT_DATE / ADD_MONTHS / TO_CHARサーバーとセッションの時間基準、戻り値の型
数値ROUND / CEIL / FLOOR / ABS / MOD / TRUNCFLOORと小数桁の切捨ては別
条件・NULLCASE式 / COALESCE / NULLIF / NVL型を揃え、NULLはIS NULLで判定
独自処理PL/SQLのストアド関数CREATE FUNCTIONはDB定義の変更
各行の順位・累積RANK / DENSE_RANK / ROW_NUMBER / SUM OVER同順位と行順・ウィンドウを区別

Oracle 19cのSQL関数一覧から個々の構文へ進めます。

集計関数 (Aggregate Functions)

COUNT、SUM、AVG、MIN、MAXは行の集合を集計します。以下の共通部分は、同名の表・列が存在する場合の例です。末尾のGROUP_CONCATの1文だけはMySQL用で、Oracleではそのまま使いません。PostgreSQLのSTRING_AGGも別の構文です。

共通SQLとMySQL比較の保存例です。最後のGROUP_CONCATはOracleでは使えません。GROUP BYの並びには同値の順序保証もありません。Oracleの連結集計は別枠のLISTAGGを参照してください。

-- 件数
SELECT COUNT(*) FROM orders;            -- NULL も含めた全行
SELECT COUNT(email) FROM users;          -- NULL を除いた件数
SELECT COUNT(DISTINCT category) FROM products;

-- 合計・平均・最大・最小
SELECT SUM(amount), AVG(amount), MIN(amount), MAX(amount)
FROM orders;

-- グループ別集計
SELECT category, COUNT(*) AS cnt, SUM(price) AS total
FROM products
GROUP BY category
HAVING COUNT(*) > 5
ORDER BY total DESC;

-- 文字列を連結する集計(MySQL: GROUP_CONCAT / PostgreSQL: STRING_AGG)
SELECT category, GROUP_CONCAT(name ORDER BY price SEPARATOR ', ')
FROM products GROUP BY category;
関数動作Oracle 19cでのNULL・0件の扱い
COUNT(*)全行数列がNULLの行も数える。対象0行なら0
COUNT(col)非NULL件数NULLを除く。対象0行なら0
SUM(col)合計NULLを除く。対象なし・全てNULLならNULL
AVG(col)平均NULLを分母にも含めない。対象なし・全てNULLならNULL
MIN / MAX(col)最小 / 最大NULLを除く。対象なし・全てNULLならNULL

この0件の説明はGROUP BYなしの集計を想定しています。GROUP BYで該当するグループ自体がなければ、集計行も返りません。AVG(amount)とAVG(NVL(amount, 0))は分母が変わるため、同じ意味ではありません。集計関数の仕様

Oracleで文字列を集約する場合はLISTAGGを使えます。次はproductsに一意なidがある場合の追加例です。文字列の上限を超えると既定ではエラーになるので、大量データを無条件に1文字列へまとめないでください。

-- Oracle Database 19c / products(id, category, name, price)を想定
SELECT category,
       LISTAGG(name, ', ') WITHIN GROUP (ORDER BY price, id) AS names
FROM products
GROUP BY category
ORDER BY category;

LISTAGG内のORDER BYは連結順、外側は結果行の順です。LISTAGG

文字列関数

Oracle 19cのLENGTHは文字数、LENGTHBはバイト数です。CHAR型では末尾の埋め草の空白も長さに含まれます。NULL入力の長さはNULLであり、0ではありません。LENGTHBはマルチバイト文字セットのCLOB/NCLOBには使えません。LENGTH

CONCATは2引数なので、氏名と区切りの3項を直接渡さず、入れ子にするか連結演算子を使います。Oracleの部分文字列はSUBSTRです。CONCAT、SUBSTR

-- Oracle Database 19c / users(first_name, last_name, email, name)を想定
SELECT first_name || ' ' || last_name AS full_name,
       CONCAT(CONCAT(first_name, ' '), last_name) AS same_form,
       SUBSTR(email, 1, 3) AS prefix,
       LENGTH(name) AS character_count,
       LENGTHB(name) AS byte_count
FROM users;

MySQLを中心とした文字列比較

次の枠はMySQL 8.4の例です。PostgreSQLのコメントは比較用です。3引数CONCAT、CHAR_LENGTH、LOCATE、REVERSEやFROMなしの定数SELECTをOracle 19c用の構文一覧として転用しないでください。Oracleでは定数の例にもFROM DUALを付けます。日本語1文字のバイト数は文字・文字セットに依存し、常に3とは限りません。MySQL文字列関数

MySQL 8.4の比較例です。3引数CONCAT・CHAR_LENGTH・LOCATE・REVERSEとFROMなしの定数SELECTをOracle 19cへそのまま移せません。LENGTHのバイト数というコメントもMySQL限定です。

-- 連結
SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM users;
-- PostgreSQL は || も使える: first_name || ' ' || last_name

-- 部分文字列(1 始まり、長さ指定)
SELECT SUBSTRING(email, 1, 3) FROM users;
-- MySQL: SUBSTRING / SUBSTR / MID
-- PostgreSQL: SUBSTRING(email FROM 1 FOR 3)

-- 大文字・小文字変換
SELECT UPPER(name), LOWER(email) FROM users;

-- 長さ
SELECT LENGTH(name) FROM users;          -- バイト数(MySQL)
SELECT CHAR_LENGTH(name) FROM users;     -- 文字数

-- 前後の空白除去
SELECT TRIM(' Hello ');                  -- 'Hello'
SELECT LTRIM(' Hello'), RTRIM('Hello ');

-- 置換
SELECT REPLACE(email, '@old.com', '@new.com') FROM users;

-- パディング
SELECT LPAD('5', 3, '0');                -- '005'
SELECT RPAD('A', 5, '-');                -- 'A----'

-- 位置検索
SELECT INSTR(email, '@') FROM users;     -- '@' の位置(1 始まり)
SELECT LOCATE('@', email) FROM users;    -- MySQL

-- 文字列を逆順
SELECT REVERSE('Hello');                 -- 'olleH'

日時関数

やりたいことOracle 19c注意
DBサーバーOSの現在日時SYSDATEDATE型。引数括弧なし
セッションのタイムゾーンで現在日時CURRENT_DATEDATE型。MySQLのCURDATEのような日付だけの値ではない
日を加えるDATE値 + 日数時刻を含むDATE値では日数の小数部分も意味を持つ
月を加えるADD_MONTHS(date, n)NLS_CALENDARに依存。月末の補正がある
DATE同士の日数差date2 - date1時刻差を含み得る。MySQL DATEDIFFの日付部分だけの差とは異なる
表示用の文字列にするTO_CHAR(date, format)戻り値は文字列。保存型や時間基準の変更ではない
年などを取り出すEXTRACTDATEからは年・月・日。時・分・秒はTIMESTAMP等を使う

SYSDATE、CURRENT_DATE、日時型と演算、ADD_MONTHS、TO_CHAR

EXTRACTはOracleのDATEをANSI DATEとして扱うため、DATEに時刻が保存されていてもHOURなどは抽出できません。EXTRACT

-- Oracle Database 19c / 表を変更しない説明例
SELECT SYSDATE AS server_now,
       CURRENT_DATE AS session_now,
       SYSDATE + 7 AS seven_days_later,
       ADD_MONTHS(DATE '2026-01-31', 1) AS next_month,
       DATE '2026-12-31' - DATE '2026-06-11' AS days_between,
       TO_CHAR(SYSDATE, 'YYYY/MM/DD HH24:MI') AS display_time,
       EXTRACT(YEAR FROM DATE '2026-06-11') AS year_part
FROM DUAL;

ADD_MONTHSの月末結果をグレゴリオ暦として読む場合はNLS_CALENDARの設定も確認します。DATEは時刻を持ちますがタイムゾーンを保持する型ではありません。TIMESTAMP同士の差などを、DATE同士の数値の日数差と同一視しないでください。

MySQL / PostgreSQLの日時比較

次の枠は主にMySQL 8.4用です。「PostgreSQL は TO_CHAR」の1文はPostgreSQL用として別に読みます。NOW、CURDATE、CURTIME、DATE_ADD、DATE_SUB、DATEDIFF、TIMESTAMPDIFF、DATE_FORMATと末尾のNOWを使った抽出はOracleの書き方ではありません。コメントの2026-06-11等は固定された出力例で、現在日時を保証する値ではありません。MySQL DATEDIFFは時刻部分を計算に含めません。MySQL日時関数、PostgreSQL書式変換

主にMySQL 8.4、TO_CHARの1文はPostgreSQLの比較例です。NOW・DATE_ADD・DATEDIFF等はOracleの構文ではありません。固定日時コメントは今回の実行結果ではありません。

-- 現在日時
SELECT NOW();                            -- 2026-06-11 12:34:56
SELECT CURDATE(), CURRENT_DATE;          -- 2026-06-11
SELECT CURTIME(), CURRENT_TIME;          -- 12:34:56

-- 加算・減算
SELECT DATE_ADD(NOW(), INTERVAL 7 DAY);
SELECT DATE_SUB(NOW(), INTERVAL 1 MONTH);
SELECT NOW() + INTERVAL 1 HOUR;          -- MySQL の省略形

-- 差分
SELECT DATEDIFF('2026-12-31', '2026-06-11');  -- 日数
SELECT TIMESTAMPDIFF(MONTH, birth, NOW())     -- 月単位 (MySQL)
FROM users;

-- フォーマット
SELECT DATE_FORMAT(NOW(), '%Y/%m/%d %H:%i');  -- '2026/06/11 12:34'
SELECT DATE_FORMAT(NOW(), '%W');              -- 'Thursday'

-- PostgreSQL は TO_CHAR
SELECT TO_CHAR(NOW(), 'YYYY/MM/DD HH24:MI');

-- 一部要素抽出
SELECT YEAR(NOW()), MONTH(NOW()), DAY(NOW()),
       HOUR(NOW()), MINUTE(NOW()), SECOND(NOW());
SELECT EXTRACT(YEAR FROM NOW());         -- 標準 SQL

数値関数

Oracleでは小数桁を指定した切捨てはTRUNCです。FLOORは値以下の最大の整数なので、負数を0方向へ切り詰める操作とは違います。MySQLのTRUNCATE関数や剰余の%表記をそのままOracleへ置換しないでください。TRUNC(number)、FLOOR

-- Oracle Database 19c
SELECT ROUND(3.567, 2) AS rounded,
       TRUNC(3.567, 1) AS truncated,
       FLOOR(-3.8) AS lower_integer,
       TRUNC(-3.8) AS toward_zero,
       ABS(-7) AS absolute_value,
       MOD(10, 3) AS remainder
FROM DUAL;

MySQLを中心とした数値比較

次はMySQLの表記を中心とした例で、コメントのCEILING / POW / % / RANDをOracleの別名一覧としては使いません。PostgreSQLの乱数関数名RANDOMは比較です。乱数の整数化は秘密値生成や暗号学的安全性を保証するものではありません。MySQL数値関数

MySQL中心の数値比較例です。TRUNCATE・CEILING・POW・%・RANDをOracleの同名・別名関数として転用しません。OracleではTRUNCとFLOORの負数での違いにも注意してください。

SELECT ROUND(3.567, 2);          -- 3.57(小数 2 桁)
SELECT CEIL(3.2);                -- 4(切り上げ)= CEILING
SELECT FLOOR(3.8);               -- 3(切り捨て)
SELECT TRUNCATE(3.567, 1);       -- 3.5(切り捨て、桁指定)MySQL

SELECT ABS(-7);                  -- 7
SELECT MOD(10, 3);               -- 1(余り)= 10 % 3
SELECT POWER(2, 10);             -- 1024 = POW
SELECT SQRT(16);                 -- 4

SELECT RAND();                   -- 0.0 〜 1.0 の乱数(MySQL)= RANDOM (PG)
SELECT FLOOR(RAND() * 100);      -- 0〜99 の整数乱数

SELECT SIGN(-5), SIGN(0), SIGN(7);  -- -1, 0, 1

条件関数 / NULL 関数

CASEは条件式であり、任意のDBで同じ名前の関数を呼ぶという意味ではありません。OracleではCOALESCEで最初の非NULL値、NVLでNULL時の代替値、NULLIFで同値時のNULLを得られます。型の変換規則まで相互に同じとは限らないため、日付と文字列、数値と文字列を無条件に混ぜないでください。COALESCE、NVL、NULLIF

Oracle 19cでは長さ0の文字列はNULLとして扱われます。NULLと0は別物で、NULLの検査はIS NULL / IS NOT NULLを使います。通常の比較や算術にNULLが入る場合と、文字列連結の扱いは分けて考えます。NULLの扱い

-- Oracle Database 19c / users(nickname, name, phone)を想定
SELECT COALESCE(nickname, name, 'Anonymous') AS display_name,
       NVL(phone, '不明') AS display_phone
FROM users;

SELECT NULLIF(10, 10) AS null_value,
       CASE WHEN CAST(NULL AS NUMBER) IS NULL THEN 'missing' ELSE 'present' END AS state
FROM DUAL;

CASE / COALESCEと製品固有の比較

次の枠のCASEとusersに対するCOALESCEは、列の型が適切ならOracleでも使う考え方です。ただしNULLIF(deleted_at, '0000-00-00')の行はFROMも型定義もなく、実行用の完成SQLではありません。Oracle DATEに存在しないゼロ日付を入れて処理する例としては使えません。実在する日付同士を比較するなら同じDATE型へ揃えます。

IFNULLとIFはMySQL、2引数ISNULLはSQL Server用です。「同等」「相当」はNULL置換の目的の比較で、戻り値の型や長さまで同一という意味ではありません。SQL Server ISNULL

混在製品の保存例です。NULLIF(deleted_at, '0000-00-00')はFROMと型定義がない未完成SQLで、Oracle DATEの例ではありません。IFNULL/IFはMySQL、2引数ISNULLはSQL Serverです。

-- CASE WHEN: 標準 SQL
SELECT name,
       CASE
           WHEN score >= 80 THEN 'A'
           WHEN score >= 60 THEN 'B'
           WHEN score >= 40 THEN 'C'
           ELSE 'D'
       END AS grade
FROM students;

-- COALESCE: 最初の非 NULL を返す
SELECT COALESCE(nickname, name, 'Anonymous') FROM users;

-- NULLIF: 2 値が同じなら NULL を返す
SELECT NULLIF(deleted_at, '0000-00-00');

-- MySQL: IFNULL(COALESCE と同等の 2 引数版)
SELECT IFNULL(phone, '不明') FROM users;

-- MySQL: IF
SELECT IF(score > 60, '合格', '不合格') FROM students;

-- SQL Server: ISNULL(MySQL の IFNULL 相当、ただし PG の ISNULL とは別物)
SELECT ISNULL(phone, '不明') FROM users;  -- SQL Server

ユーザー定義関数 (UDF)

OracleではPL/SQLでストアド関数を定義できます。次はNUMBERを受け取りNUMBERを返す独立した説明例です。CREATEはDB定義を変更する操作であり、既存の業務DBへ貼り付ける接続確認用SQLではありません。CREATE FUNCTION

-- Oracle Database 19c / SQL*Plus等のスクリプト実行を想定
CREATE FUNCTION calc_tax_price_oracle(p_price NUMBER, p_rate NUMBER)
RETURN NUMBER
IS
BEGIN
    RETURN FLOOR(p_price * (1 + p_rate));
END;
/

末尾のスラッシュはSQL*Plus等のクライアントの実行区切りで、ドライバへ送る関数本体とは分けます。税計算は端数処理の業務要件・税率の適用条件を省いた学習例で、実務の税額計算の保証ではありません。SQL*Plusのスラッシュ、PL/SQL CREATE FUNCTION

MySQL / PostgreSQLの関数定義比較

次の枠はMySQLの定義と使用例、その後にPostgreSQLの別定義を並べたものです。同じ接続で連続実行する1本のスクリプトではありません。DELIMITERはmysqlクライアント側の区切り指示でOracleの文法ではなく、$$ LANGUAGE plpgsqlはPostgreSQLです。MySQLのDETERMINISTIC宣言は実装の正しさを検査する保証ではありません。整数の戻り型・引数精度・NULL入力・権限・バイナリログ等の条件も製品別に確認します。MySQL CREATE FUNCTION、PL/pgSQLの構造

MySQLとPostgreSQLの独立した関数定義比較です。DELIMITERと$$ LANGUAGE plpgsqlはOracleの構文ではなく、連続実行用でもありません。DETERMINISTICは実装の正しさを検証しません。

-- MySQL: 税込価格計算関数
DELIMITER //
CREATE FUNCTION calc_tax_price(price INT, rate DECIMAL(3,2))
RETURNS INT
DETERMINISTIC
BEGIN
    RETURN FLOOR(price * (1 + rate));
END //
DELIMITER ;

-- 使用
SELECT name, calc_tax_price(price, 0.10) AS tax_inc
FROM products;

-- PostgreSQL: PL/pgSQL
CREATE OR REPLACE FUNCTION calc_tax_price(price INT, rate NUMERIC)
RETURNS INT AS $$
BEGIN
    RETURN FLOOR(price * (1 + rate));
END;
$$ LANGUAGE plpgsql;

ウィンドウ関数 (OVER 句)

Oracleでは分析関数と呼び、集計で行をまとめるのとは違って各行を残して順位や累積値を計算します。次の枠のproductsのランキングは同名列があればOracle 19cでも使う形ですが、同価格の順序が一意ではありません。RANKとDENSE_RANKで同価格を同順位とするのは意図した動作で、ROW_NUMBERの割当て、LAG/LEADの前後を安定させたい場合には一意なキーを加えます。

同じ枠のsalesを使った2例は製品共通の完成SQLではありません。OracleではDATEが予約語なので、枠内の引用符なしのdateを列名として使う文はそのまま実行できません。列定義に合わせた引用が必要で、Oracle向けの例は後掲のsale_dateで示します。Oracleの予約語

ランキングと分析の保存例です。Oracleでは引用符なしのdateは予約語のためsalesの2例をそのまま使えません。同値のROW_NUMBERと前後行は不定になり得ます。RANKの同順位は維持し、別枠のsale_date/id例ではROWSを明示します。

-- カテゴリ別ランキング
SELECT name, category, price,
       RANK()       OVER (PARTITION BY category ORDER BY price DESC) AS rk,
       DENSE_RANK() OVER (PARTITION BY category ORDER BY price DESC) AS dr,
       ROW_NUMBER() OVER (PARTITION BY category ORDER BY price DESC) AS rn
FROM products;

-- 累積合計
SELECT date, amount,
       SUM(amount) OVER (ORDER BY date) AS running_total
FROM sales;

-- 直前・直後の値
SELECT date, amount,
       LAG(amount, 1)  OVER (ORDER BY date) AS prev_amount,
       LEAD(amount, 1) OVER (ORDER BY date) AS next_amount
FROM sales;

OracleのORDER BYだけを指定した分析集計は既定のRANGEフレームとなり、同じ日付の行をまとめて累積し得ます。行ごとの積上げならROWSを明示し、並びを一意にします。次はsalesのidが一意、sale_dateがDATE型という別の前提です。分析関数内の順序と最終表示順も別です。分析関数の順序とフレーム

-- Oracle Database 19c / sales(id, sale_date, amount)を想定
SELECT id, sale_date, amount,
       SUM(amount) OVER (
           ORDER BY sale_date, id
           ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
       ) AS running_total,
       LAG(amount) OVER (ORDER BY sale_date, id) AS prev_amount,
       LEAD(amount) OVER (ORDER BY sale_date, id) AS next_amount
FROM sales
ORDER BY sale_date, id;

FAQ

COUNT(*)とCOUNT(1)はどちらが速い?

COUNT(1)は非NULLの定数を各行について数えるので、全行を数える用途はCOUNT(*)と同じです。ただし、このページでは製品・実行計画を問わず同じ速度とは断定しません。COUNT(*)で意図を示し、性能は対象SQLと実行計画で確認します。COUNT

CONCATにNULLがあると必ず全体がNULLになる?

Oracle 19cの文字列連結では片方がNULLでももう片方が返り、両方がNULLならNULLになります。MySQL CONCATは引数のどれかがNULLならNULLなので同一視できません。名前が欠けたときに空白だけを残すのか、氏名全体を欠落扱いにするのかはCASEなどで明示します。Oracle連結演算子、MySQL CONCAT

LENGTHかCHAR_LENGTHか?

Oracle 19cでは文字数はLENGTH、バイト数はLENGTHBです。MySQLではLENGTHがバイト数、CHAR_LENGTHが文字数なので、同名関数だけを頼りに移植しないでください。文字数は画面上の見た目の幅とも別です。

SYSDATEとCURRENT_DATEは同じ?

時間の基準が違います。前者はDBサーバーOS、後者はセッションのタイムゾーンに基づきます。どちらもOracleではDATE型で時刻を持ち、クライアントの表示形式だけで日付専用の値だとは判断できません。

子ページ

子ページはありません

同階層のページ
  1. DDL
  2. DML
  3. トランザクション制御 (SQL)
  4. データディクショナリ
  5. 動的パフォーマンスビュー (V$ビュー)
  6. 関数

最近更新/作成されたページ