変更要約: 初版(主題S3・S3.1〜S3.3)
3.3組み込み関数(集約・文字列・時間)
集約関数(count・sum・avg・max・min)で複数行を1つの値にまとめ、算術関数・演算子で計算し、文字列関数(char_length・length・lower・upper・substring・replace・trim・連結演算子||・LIKE述語)で文字列を加工し、時間関数(age・now・current_date・current_timestamp・extract・to_char)で日時を扱う方法を学びます。
SQLの真価は、生データをそのまま返すだけでなく関数を通して意味のある形に加工できる点にあります。複数行を1件に集約する関数、文字列を整形する関数、日時を計算・整形する関数はいずれも実務のレポーティングやデータクレンジングで頻繁に使われ、OSS-DB Silverでも定番の出題領域(S3.2)です。
3.3.1集約関数と算術関数
- count=行数を数える(
count(*)は全行、count(column)はNULL以外の値の個数)。sum=数値列の合計(NULLは無視)。avg=平均値(NULLは分母から除外)。max・min=最大値・最小値。いずれもGROUP BYと組み合わせてグループごとの集計に使うのが定番。 - 算術演算子=
+(加算)・-(減算)・*(乗算)・/(除算、整数同士の除算は結果も整数に切り捨て)・%(剰余)。型の異なる列同士の演算では暗黙・明示のキャスト(::numeric等)が必要になる場面がある。
3.3.2文字列関数と時間関数
- length/char_length=文字列の長さを返す(
length(name))。lower/upper=小文字化/大文字化。substring=部分文字列抽出(substring(name from 1 for 3))。replace=文字列置換(replace(text, 'old', 'new'))。trim=前後の空白(または指定文字)除去。 - 連結演算子
||=文字列同士を結合(first_name || ' ' || last_name)。LIKE述語=パターンマッチによる絞り込み(WHERE name LIKE 'A%'で"A"から始まる、%は任意長の任意文字列・_は任意の1文字)。大文字小文字を区別しないILIKEもある。 - now/current_timestamp=現在の日時(タイムゾーン付き)を返す。current_date=現在の日付のみ。age=2つの日時の差を「◯年◯ヶ月◯日」のような間隔として返す(
age(timestamp1, timestamp2)、片方省略でnowとの差)。 - extract=日時から特定の要素(年・月・日・曜日等)を取り出す(
extract(year FROM order_date))。to_char=日時(や数値)を任意の書式の文字列へ変換(to_char(order_date, 'YYYY-MM-DD'))。表示用の整形に使われる。
「count(*)は全行・count(column)はNULL除外」「sum/avgはNULLを計算から除外」「LIKEの%は任意長・_は1文字」「to_charは書式付き文字列変換・extractは要素の切り出し」「整数同士の除算は切り捨て」が最頻出です。文字列連結||とLIKEパターンを組み合わせた実務的な文字列加工の設問も定番です。
月次レポートを作る実務シナリオで各関数の連携を見てみましょう。「今月の注文件数、合計金額、平均注文額を1行で出したい」という要求にはSELECT count(*), sum(amount), avg(amount) FROM orders WHERE extract(month FROM order_date) = extract(month FROM current_date);のように、extractで対象月を絞り込みつつ、3つの集約関数を同じSELECT文に並べます。ここでamountにNULLの行が混在していると、count(*)はNULL行も数える一方、sum(amount)とavg(amount)はNULLを計算から除外するため、同じテーブルでも関数によって「何を数えているか」が微妙に異なる点に注意が必要です。氏名を表示用に整形したい場合は、upper(substring(last_name from 1 for 1)) || lower(substring(last_name from 2))のように部分文字列抽出と大文字/小文字変換、連結演算子||を組み合わせると「先頭だけ大文字」の表記が作れます。会員登録日から現在までの経過期間を「3年2ヶ月」のような形で見せたい場合はage(age(now(), signup_date))が便利で、表示用のフォーマットを完全に制御したい場合(2026年07月のような和暦風表記等)はto_char(to_char(signup_date, 'YYYY"年"MM"月"'))を使います。検索条件で「メールアドレスがgmail.comで終わる会員」を探すならWHERE email LIKE '%@gmail.com'のようにLIKEのワイルドカード%を活用し、大文字小文字を区別したくない場合はILIKEに切り替えます。
| 関数 | 用途 | 備考 |
|---|---|---|
| count(*) / count(col) | 行数を数える | count(*)は全行・count(col)はNULL除外 |
| sum / avg | 合計・平均を計算 | NULLは計算から除外 |
| extract | 日時から要素を取り出す | 年・月・日・曜日等を数値で取得 |
| to_char | 任意書式の文字列へ変換 | 表示整形に使う |
ひっかけ: 「count(*)とcount(column)は常に同じ結果になる」は誤りです。count(column)はNULL値を数えないため、対象列にNULLが含まれる行がある場合はcount(*)より小さい値になります。また「sum関数はNULLを0として計算する」も誤り=NULLは合計計算そのものから除外されます(0として加算されるわけではなく、対象行数からも除かれる点でavgの結果にも影響します)。「LIKEの_は任意長の文字列にマッチする」も誤りで、任意長にマッチするのは%、_はちょうど1文字です。
3.3.3この節のまとめ
- count(*)=全行・count(col)/sum/avg=NULL除外。算術演算子は整数同士の除算で切り捨てに注意
- 文字列はlength/lower/upper/substring/replace/trim/
||(連結)/LIKE(%=任意長・_=1文字) - 時間はnow/current_date/current_timestamp(現在日時)・age(差分の間隔)・extract(要素抽出)・to_char(書式変換)
進捗の記録にはログインが必要です。
理解度チェック
(軽い確認用)Q1. orders テーブルの amount 列には一部NULLの行が含まれる。count(*) と count(amount) の結果について正しい説明はどれか。
Q2. メールアドレスが "test" で終わる会員を、大文字小文字を区別せずに検索したい。適切な条件式はどれか。
Q3. 会員の登録日(signup_date)から現在までの経過期間を「◯年◯ヶ月◯日」のような間隔として取得したい。最も適した関数はどれか。

