Q1日付を'2024-03-15'のような ISO 文字列で保存する利点として正しいものはどれですか。
日付時刻型と STRICT テーブル
この記事は、基礎から複雑なSQL,SQLチューニングまでSQLの実践的なスキルを1からマスターする「SQL入門講座の一部」です。
5万行のperf_salesを題材に、ISO文字列・julianday・unixepochの3つの日付保存形式とdate() / strftime()での年別・期間集計、typed_strictテーブルで型不一致が拒否されるしくみを実際に動かして学びます。
本記事で使うデータ — perf_sales
日付は文字列・数値のいずれかに保存し、date() / strftime()などの関数で取り出しや計算をします。
前半では売上テーブルperf_salesのsale_date列で日付の 3 つの保存形式と取り出しを確認し、後半では型を厳格化するSTRICTテーブルを自分で作って、型に合わない値を入れたときの挙動を確認します。
日付の 3 つの保存形式 — ISO 文字列 / julianday / unixepoch
日付時刻は主に 3 つの形式で保存できます。
| 形式 | 値の例 | 主な用途 |
|---|---|---|
| ISO 文字列 | '2024-03-15'(時刻込みなら '2024-03-15 12:30:00') | 人が読む / 並べ替え・範囲比較が文字列の大小でそのまま正しく行える(最も一般的) |
| julianday | julianday('2024-03-15') → 2460384.5(紀元前からの通日を表す小数) | 日付どうしの差を日数で計算 |
| unixepoch | strftime('%s', ...) → 1710460800(1970-01-01 からの経過秒数の整数) | 秒単位の時刻計算 / システム間のやり取り |
どの形式で保存しても、date()で'YYYY-MM-DD'に、datetime()で'YYYY-MM-DD HH:MM:SS'に変換して取り出せます。
本講座では人が読めて比較しやすいISO 文字列を基本に使い、必要なときだけ julianday / unixepoch に変換します。
-- 同じ日付を 3 形式で表す(読むだけの別例)
SELECT '2024-03-15' AS iso_text,
julianday('2024-03-15') AS as_julianday,
strftime('%s','2024-03-15 00:00:00') AS as_unixepoch;
-- 3 形式から読みやすい日付に戻す
SELECT date(julianday('2024-03-15')) AS from_jd,
datetime(1710460800,'unixepoch') AS from_unix,
date('2024-03-15') AS from_iso;
-- ISO 文字列は文字列の大小で範囲比較がそのまま正しい
SELECT '2024-03-15' BETWEEN '2024-01-01' AND '2024-12-31' AS in_2024; -- 1
date() と strftime() で日付を加工・集計する
date(値, 修飾子...): 日付部分を'YYYY-MM-DD'で返し、'+1 month'/'-7 days'/'start of month'のような修飾子で日付計算ができるstrftime(書式, 値):%Y(年)・%m(月)・%d(日)・%w(曜日)などの書式指定子で、日付の一部を文字列として取り出すstrftime('%Y', sale_date)で年だけを取り出してGROUP BYすれば、年別の集計ができるsale_dateは ISO 文字列で保存されているので、WHERE sale_date >= '2024-01-01'のような範囲条件がそのまま文字列の大小比較として正しく働く
-- 日付の加工(読むだけの別例)
SELECT date('2024-03-15','+1 month') AS plus_1_month, -- 2024-04-15
date('2024-03-15','start of month') AS month_start, -- 2024-03-01
strftime('%Y', '2024-03-15') AS year_part, -- 2024
strftime('%Y-%m','2024-03-15') AS year_month; -- 2024-03
-- 年別に件数を集計(ISO 文字列なので strftime で年を取り出して GROUP BY)
SELECT strftime('%Y', sale_date) AS yr, COUNT(*) AS cnt
FROM perf_sales
GROUP BY yr
ORDER BY yr;
STRICT テーブル — 型を厳格化する
ここまでの型親和性は、数値らしい文字列を柔軟に変換する扱い方でした。
これに対し、テーブル定義の末尾にSTRICT(厳格)を付けると、その列に宣言した型と異なる値の格納を拒否します。
数値の列に、数値化できない文字列の値を入れようとすると、暗黙変換せずにエラーになります。
型の取り違えを早い段階で検出したいテーブルで使います。
STRICTテーブルでは列に使える型がINTEGER / REAL / TEXT / BLOB / ANYに限られ、INTや独自の型名は使えません。
次の演習ではSTRICT付きのtyped_strictテーブルを自分で作り、その後の演習で型の合う値と合わない値を入れて、挙動の差を体感します。
-- STRICT テーブルの宣言と挙動(読むだけの別例)
CREATE TABLE IF NOT EXISTS typed_strict(a INTEGER, b TEXT, c REAL) STRICT;
-- 型が合う行は問題なく入る
INSERT INTO typed_strict VALUES (1, '2024-03-15', 1.5);
-- a INTEGER 列に数値化できない文字列を入れると
-- 暗黙変換されず、INSERT がエラーになる:
-- cannot store TEXT value in INTEGER column typed_strict.a
INSERT INTO typed_strict VALUES ('not-a-number', 'x', 1.0);
理解度チェック
まずは1問ずつ答えてみましょう。
Q2strftime('%Y', sale_date)の役割として正しいものはどれですか。
Q3STRICTを付けたテーブルのINTEGER列に、数値化できない文字列を INSERT しようとしたときの説明として正しいものはどれですか。