日付関数(EXTRACT で年月を取り出す)の使い方
shain社員
| hired_on |
|---|
| 2014-04-01 |
| 2016-10-01 |
| 2019-04-01 |
| 2020-04-01 |
| 2024-04-01 |
| 2018-04-01 |
| … |
EXTRACT で作った列を足す年を取り出した結果
| hired_on | nen |
|---|---|
| 2014-04-01 | 2014 |
| 2016-10-01 | 2016 |
| 2019-04-01 | 2019 |
| 2020-04-01 | 2020 |
| 2024-04-01 | 2024 |
| 2018-04-01 | 2018 |
| … | |
EXTRACT(YEAR FROM hired_on) AS nen
- 青い列= 式から作った列。元の表には無い列です
- 社員 18 行 → 年を取り出した結果 18 行(行数は変わりません。減らすのは WHERE の仕事です)
行は 1 行も減っていません。日付を切り出して新しい列を作っているだけです。
日付から「年」や「月」だけを取り出すのが EXTRACT です。 年月ごとの集計はこれで書きます。ただし絞り込みに使うと索引が効かなくなるので、「まとめるときは EXTRACT、絞るときは範囲」と覚えてください。
この書き方が使えない製品があります そのまま使える: PostgreSQL / MySQL / MariaDB
- SQLite では使えません(EXTRACT(日付の一部を取り出す)・DATE 'YYYY-MM-DD' の日付リテラル)
- SQL Server では使えません(EXTRACT(日付の一部を取り出す)・DATE 'YYYY-MM-DD' の日付リテラル)
- Oracle Database では使えません(DATE 'YYYY-MM-DD' の日付リテラル)
未確認: IBM Db2
このページの実行結果と図は、すべて本物のデータベースに同じ SQL を流して得たものです。手で書いた「たぶんこうなる」ではありません。
01EXTRACT で年・月・日を取り出す
結論: EXTRACT(取り出す部分 FROM 日付) と書きます。返るのは数値です。
EXTRACT(YEAR FROM hired_on)… 年EXTRACT(MONTH FROM hired_on)… 月EXTRACT(DAY FROM hired_on)… 日
FROM が入る独特な書き方ですが、これが標準 SQL の形です。カンマ区切りではないので注意してください。
⚠️ この書き方は製品差が大きい部分です。 SQLite と SQL Server には EXTRACT がありません(それぞれ strftime、DATEPART を使います)。使えない製品がある例には自動で注記が出ます。
SELECT EXTRACT(YEAR FROM hired_on) AS nen,日付から年を切り出すcount(*) AS ninzuまとまりごとに数えるFROM shainどの表を見るかGROUP BY EXTRACT(YEAR FROM hired_on)同じ年をひとまとめにするORDER BY nen年の順に並べる
GROUP BY にも同じ式を書きます。SELECT で付けた別名を GROUP BY に書けるかは製品によって違うので、式をそのまま書くほうが確実です。
SELECT name, hired_on,
EXTRACT(YEAR FROM hired_on) AS nen,
EXTRACT(MONTH FROM hired_on) AS tsuki
FROM shain
ORDER BY shain_id;| name | hired_on | nen | tsuki |
|---|---|---|---|
| 鈴木 | 2014-04-01 | 2014 | 4 |
| 佐藤 | 2016-10-01 | 2016 | 10 |
| 田中 | 2019-04-01 | 2019 | 4 |
| 中村 | 2020-04-01 | 2020 | 4 |
| 小林 | 2024-04-01 | 2024 | 4 |
| 加藤 | 2018-04-01 | 2018 | 4 |
(18 行 … うち先頭 6 行を表示)
日付はそのまま残しつつ、年と月の列が増えています。値は数値なので、そのまま計算や比較に使えます。
⚠️ この書き方は SQLite / SQL Server では使えません(EXTRACT(日付の一部を取り出す))。製品ごとの対応表
02年や月でまとめて数える
結論: GROUP BY に EXTRACT の式を書けば、年ごと・月ごとの集計になります。
これが EXTRACT のいちばん多い使い道です。日付をそのまま GROUP BY に書くと 1 日ごとに分かれてしまうので、まとめたい粒度まで削ってからグループにします。
次の例は入社月の分布です。4 月に 13 人が集中していることが 1 目で分かります。生の日付を眺めていても気づけない偏りが、粒度を落とすと見えてきます。
SELECT EXTRACT(YEAR FROM hired_on) AS nen,
count(*) AS ninzu
FROM shain
GROUP BY EXTRACT(YEAR FROM hired_on)
ORDER BY nen;| nen | ninzu |
|---|---|
| 2013 | 1 |
| 2014 | 1 |
| 2015 | 1 |
| 2016 | 2 |
| 2017 | 1 |
| 2018 | 1 |
| 2019 | 1 |
| 2020 | 2 |
| 2021 | 1 |
| 2022 | 1 |
| 2023 | 1 |
| 2024 | 1 |
| 2025 | 1 |
| 2026 | 3 |
(14 行)
14 行です。1 人ずつの年が多いなか、2016 年と 2020 年が 2 人、2026 年が 3 人です。
⚠️ この書き方は SQLite / SQL Server では使えません(EXTRACT(日付の一部を取り出す))。製品ごとの対応表
SELECT EXTRACT(MONTH FROM hired_on) AS tsuki,
count(*) AS ninzu
FROM shain
GROUP BY EXTRACT(MONTH FROM hired_on)
ORDER BY tsuki;| tsuki | ninzu |
|---|---|
| 4 | 13 |
| 5 | 1 |
| 6 | 1 |
| 10 | 3 |
(4 行)
4 行しかありません。18 人のうち 13 人が 4 月入社で、あとは 5 月・6 月・10 月だけです。
⚠️ この書き方は SQLite / SQL Server では使えません(EXTRACT(日付の一部を取り出す))。製品ごとの対応表
03⚠️ よくある間違い:絞り込みに EXTRACT を使わない
結論: 「2026 年に入社した人」を WHERE EXTRACT(YEAR FROM hired_on) = 2026 と書かないでください。 結果は正しいのに、遅くなります。
理由は、列を関数で包むと、その列の索引が使えなくなるからです。索引は hired_on の値そのものに対して作られているので、「hired_on を加工した結果」で探すことはできません。表の全行を 1 つずつ計算して確かめることになります。
18 行なら一瞬ですが、100 万行なら体感で分かるほど変わります。
同じ結果を範囲で書けます。 こちらは hired_on をそのまま比べているので索引が使えます。
- ✗
WHERE EXTRACT(YEAR FROM hired_on) = 2026 - ○
WHERE hired_on >= DATE '2026-01-01' AND hired_on < DATE '2027-01-01'
⚠️ 上限は翌年の 1 月 1 日未満にします。<= DATE '2026-12-31' にすると、列が時刻を持つ型のときに大晦日ぶんが落ちます。
SELECT count(*) AS n
FROM shain
WHERE EXTRACT(YEAR FROM hired_on) = 2026;| n |
|---|
| 3 |
(1 行)
3 人と正しく出ます。結果は間違っていませんが、この形では索引をたどれません。
⚠️ この書き方は SQLite / SQL Server では使えません(EXTRACT(日付の一部を取り出す))。製品ごとの対応表
SELECT count(*) AS n
FROM shain
WHERE hired_on >= DATE '2026-01-01'
AND hired_on < DATE '2027-01-01';| n |
|---|
| 3 |
(1 行)
同じ 3 人です。hired_on をそのまま比べているので、索引がある表では絞り込みに使えます。
⚠️ この書き方は SQLite / SQL Server / Oracle Database では使えません(DATE 'YYYY-MM-DD' の日付リテラル)。製品ごとの対応表
04日付の書き方は型を明示する
結論: 日付を書くときは DATE '2026-01-01' の形が標準です。
単なる文字列 '2026-01-01' でも多くの製品では自動で日付に直されて通ります。しかし書式の解釈は設定で変わります。'01/02/2026' が 1 月 2 日なのか 2 月 1 日なのかは、環境しだいです。
DATE を前に置いて型を明示しておけば、読む人にも機械にも意図が伝わります。年月日は必ず 4 桁の年から書く(YYYY-MM-DD)のも同じ理由です。
⚠️ この書き方も製品差があります。SQLite と SQL Server には DATE 'YYYY-MM-DD' という形がありません。それぞれ文字列として書きます。
SELECT name, hired_on
FROM shain
WHERE hired_on >= DATE '2025-01-01'
ORDER BY hired_on;| name | hired_on |
|---|---|
| 松本 | 2025-04-01 |
| 木村 | 2026-04-01 |
| 渡辺 | 2026-05-01 |
| 大野 | 2026-06-01 |
(4 行)
4 人です。日付は数と同じように大小を比べられます。
⚠️ この書き方は SQLite / SQL Server / Oracle Database では使えません(DATE 'YYYY-MM-DD' の日付リテラル)。製品ごとの対応表
05このページで扱っていないこと
結論: 日付の計算(引き算・加算)と「今日」の取得は、製品差がとても大きい部分です。
- 2 つの日付の差 … PostgreSQL は引き算で日数が出ますが、SQL Server や MySQL は
DATEDIFFを使い、引数の順序も戻り値の単位も違います - 何日後・何か月後 …
INTERVAL、DATEADD、DATE_ADDと書き方が分かれます - 今日の日付 …
CURRENT_DATE、GETDATE()、SYSDATEなどがあります
このサイトでは構文ごとに実際の対応を調べて注記を出す方式をとっており、これらはまだ対応表に載せていません。根拠を示せない注記は出さない方針なので、ここでは扱わずに練習問題側で個別に説明しています。
⚠️ また、「今日」を使う例はこのページには載せられません。実行結果は事前に生成して保存しているため、日によって答えが変わる式を載せると、書いてある結果と自分で打った結果が食い違うからです。
SELECT name, hired_on, taishoku_on
FROM shain
WHERE taishoku_on IS NOT NULL
ORDER BY shain_id;| name | hired_on | taishoku_on |
|---|---|---|
| 高橋 | 2021-04-01 | 2025-09-30 |
| 山本 | 2016-04-01 | 2024-03-31 |
(2 行)
2 人です。この 2 人の在籍日数を出す書き方は、製品によって変わります。練習問題「2 つの日付の差を出す」で扱っています。
自分で打ってみる
このページの例と同じデータが入った本物のデータベースを、この場(あなたのブラウザの中)で起動できます。打った SQL がサーバーへ送られることはありません。
押すと初回だけ約 9MB を読み込みます(2 回目以降はキャッシュされます)。
解いてみる
読んだだけでは書けるようになりません。実行結果で採点します。
ここで出てくる関数を引く
関連するトピック
- BETWEEN 述語期間で絞るときの境界の扱いを詳しく扱います。
- GROUP BY 句年月でまとめるときの土台です。
- 比較演算子日付の比較そのものの話です。
- 集約関数まとめた各年で何を数えるかの話です。
根拠(一次情報)
- PostgreSQL 18 マニュアル: 日付/時刻関数と演算子(EXTRACT)一次情報・確認 2026-08-03
- PostgreSQL 18 マニュアル: 式に対する索引一次情報・確認 2026-08-03
- Microsoft SQL Server: DATEPART(Transact-SQL)一次情報・確認 2026-08-03