SQLでDATE型のデータをINSERTする詳細解説

SQLで日付をINSERTする直接の答えは、手書きSQLならDBMSが正式にサポートする曖昧でない日付リテラルを使い、アプリからなら文字列連結ではなく型付きパラメーターを使うことです。PostgreSQL、MySQL、OracleではDATE '2026-07-12'、SQL ServerではISO 8601の'2026-07-12'をdate列へ渡せます。SQLiteには専用のDATEストレージ型がないため、保存形式を設計で決めます。

DATEという名前でも製品間で意味は同じではありません。PostgreSQL、MySQL、SQL ServerのDATEは日単位ですが、Oracle DatabaseのDATEは時・分・秒も保持します。さらに、NULLと'NULL'は別物です。引用符付きは文字列であり、欠損値ではありません。

目次

DBMS別の判断表

DBMSDATEの意味安全な手書き例注意点
PostgreSQL日付のみDATE '2026-07-12'曖昧な月日順はDateStyleに依存
MySQL 8.4日付のみDATE '2026-07-12'無効日付・ゼロ日付はSQLモード確認
SQL Server日付のみ'2026-07-12'DATE '...'構文は使わない
Oracle Database日付と時・分・秒DATE '2026-07-12'リテラルは午前0時だが列は時刻を保持
SQLite専用型なし'2026-07-12'をTEXT方針で保存型宣言だけでは妥当性を保証しない

基本テーブルと確認事項

例ではemployees(employee_id, employee_name, hire_date)を使います。実行前に、hire_dateの実際の型、NULL許容、DEFAULT、CHECK制約、トリガーを確認してください。「INSERTが失敗するから」という理由だけでNOT NULL制約を外すのは危険です。採用日が必須という業務ルールまで弱めてしまいます。

  • 列は本当にDATEか、DATETIME/TIMESTAMPか、文字列か
  • 日だけを保存するか、時刻・タイムゾーンも必要か
  • 欠損をNULLで表せるか、DEFAULTを使うか
  • 現在日付はサーバー日、利用者の現地日、業務日のどれか
  • アプリ入力は型付きパラメーターで渡されるか

PostgreSQLでDATEをINSERTする

PostgreSQLでは、型を明示した日付リテラルが読みやすく、DateStyleに依存しにくい方法です。

INSERT INTO employees (employee_name, hire_date)
VALUES ('山田 太郎', DATE '2026-07-12');

PostgreSQL公式マニュアルは1999-01-08のようなISO 8601形式を、どのDateStyleでも解釈できる推奨形式として示しています。'01/02/03'のような文字列はMDY、DMY、YMDで意味が変わるため使わないでください。

登録した行をすぐ確認するならRETURNINGを使えます。

INSERT INTO employees (employee_name, hire_date)
VALUES ('山田 太郎', DATE '2026-07-12')
RETURNING employee_id, employee_name, hire_date;

MySQLでDATEをINSERTする

MySQL 8.4は標準SQLの型付き日付リテラルを認識します。区切りを省いた数値や緩い形式も受理する場合がありますが、移植性と監査性のため年4桁、月日2桁、ハイフン区切りに統一します。

SELECT @@SESSION.sql_mode;

MySQLでは無効日付、ゼロ日付、警告とエラーの扱いがsql_modeに影響されます。まずINSERT前の事前確認として、同じセッションでSELECT @@SESSION.sql_mode;を単独実行します。strict modeでは、不正な日付が警告ではなくINSERTエラーになる場合があります。検証用INSERTの条件を失わないよう、INSERT直後に、間へSELECTなどの非診断文を挟まずSHOW WARNINGSを実行してください。

INSERT INTO employees (employee_name, hire_date)
VALUES ('山田 太郎', DATE '2026-07-12');
SHOW WARNINGS;

ALLOW_INVALID_DATESは月が1〜12、日が1〜31かという限定的な検査へ緩めるため、存在しない日付を受理し得ます。日付品質が必要な業務データで、INSERTを通すためだけに有効化しないでください。'0000-00-00'も通常の不明日付の代わりにせず、欠損が許されるならNULLを使います。

SQL ServerでdateをINSERTする

SQL Serverのdateはyyyy-MM-ddとyyyyMMddをISO 8601形式としてサポートします。T-SQLではPostgreSQLなどのDATE '...'構文を使いません。

INSERT INTO employees (employee_name, hire_date)
VALUES (N'山田 太郎', '2026-07-12');

入力と保存値を同時に確認するならOUTPUTを使えます。

INSERT INTO employees (employee_name, hire_date)
OUTPUT INSERTED.employee_id,
       INSERTED.employee_name,
       INSERTED.hire_date
VALUES (N'山田 太郎', '2026-07-12');

'07/12/26'や'12-07-2026'は言語とDATEFORMATで解釈が変わります。四桁年のISO形式を使います。アプリからは文字列に整形せず、date型のパラメーターへ渡す方が確実です。

Oracle DatabaseでDATEをINSERTする

OracleのANSI日付リテラルはNLS_DATE_FORMATに依存せず、形式はYYYY-MM-DDです。

INSERT INTO employees (employee_name, hire_date)
VALUES ('山田 太郎', DATE '2026-07-12');

この日付リテラルには時刻部分がなく、保存時は午前0時です。ただしOracleのDATE列は年・月・日だけでなく時・分・秒も保持します。別の処理がSYSDATEや時刻付き値を入れていると、画面が日付だけを表示していても時刻が残ります。

SELECT employee_id,
       TO_CHAR(hire_date, 'YYYY-MM-DD HH24:MI:SS') AS stored_value
FROM employees
WHERE employee_id = :employee_id;

外部入力が12/07/2026のような文字列しかない場合は、暗黙変換へ渡さずTO_DATE(value, 'DD/MM/YYYY')のように書式を明示します。ただしアプリ側でバインドできるなら、文字列変換より型付きパラメーターを優先します。

SQLiteで日付を保存する

SQLiteには日付・時刻専用のストレージクラスがありません。公式資料はISO 8601テキスト、ユリウス日、Unix timestampなどを選べると説明しています。日付だけを並べ替え・比較する用途では、固定長のYYYY-MM-DDテキストが扱いやすい方法です。

INSERT INTO employees (employee_name, hire_date)
VALUES ('山田 太郎', '2026-07-12');

列をDATEと宣言しても、他DBMSのような日付型検査が自動で付くわけではありません。アプリで実在日を検証し、保存形式を一つに固定し、必要ならスキーマのCHECK制約とSTRICTテーブルを設計してください。単純なdate(value)だけの検査で全ての暦上の不正値を防げると断定せず、採用するSQLite版でテストします。

NULL・DEFAULT・列省略の違い

書き方意味前提
NULLSQLの欠損値列がNULLを許可
'NULL'4文字の文字列DATE列では使わない
列をINSERT一覧から省略DEFAULTを適用既定値とNULL許容を確認
DEFAULTその列の既定値を要求構文対応とDEFAULT定義を確認

採用日が未定でNULLを許す設計なら、引用符なしで指定します。

INSERT INTO employees (employee_name, hire_date)
VALUES ('山田 太郎', NULL);

列を省略した場合は「NULLを入れる」とは限りません。DEFAULT CURRENT_DATEがあれば現在日が入り、既定値がなくNOT NULLならエラーになり得ます。意図が欠損なのか、既定値なのかをSQLで区別してください。

現在日付をINSERTする

現在日付は、どのタイムゾーンの「今日」かを決めてから使います。DBサーバー、接続セッション、アプリ利用者の現地時間、業務締め日の基準が違うと、深夜帯に1日ずれます。

DBMS日だけを得る例基準
PostgreSQLCURRENT_DATE現在トランザクションとセッション設定
MySQLCURRENT_DATEセッションのタイムゾーン
SQL ServerCAST(SYSDATETIME() AS date)サーバーのローカル日時
OracleTRUNC(CURRENT_DATE)セッションのタイムゾーン
SQLitedate('now')UTC。ローカルなら'localtime'修飾

OracleのCURRENT_DATEはDATE値で時刻も含むため、日だけを保存したい例ではTRUNCします。SQLiteで端末のローカル日が必要ならdate('now', 'localtime')ですが、複数地域の利用者がいるシステムでは端末任せにせず、業務タイムゾーンを設計します。

アプリではPreparedStatementを使う

利用者入力をSQL文字列へ連結すると、日付書式の問題に加えてSQLインジェクションの原因になります。JDBCではPreparedStatementへ値をバインドし、日付はsetDateでSQL DATEとして送れます。

String sql =
    "INSERT INTO employees (employee_name, hire_date) VALUES (?, ?)";

try (PreparedStatement ps = connection.prepareStatement(sql)) {
    ps.setString(1, employeeName);
    ps.setDate(
        2,
        java.sql.Date.valueOf(
            java.time.LocalDate.of(2026, 7, 12)
        )
    );

    int rows = ps.executeUpdate();
    if (rows != 1) {
        throw new SQLException("想定外の更新件数: " + rows);
    }
}

PreparedStatementは値をSQL構文ではなくパラメーターとして扱います。トランザクションを自動コミットにするか、明示コミットにするかはアプリ設計に従います。複数処理をまとめる場合は、例外時にロールバックし、接続を閉じる前に状態を確定してください。

トランザクション内で安全に試す

MySQLでトランザクション試験を始める前に、次の読み取り専用SELECTで対象テーブルのストレージエンジンを確認します。結果が空なら、接続中のデータベース名とテーブル名も見直してください。

SELECT ENGINE
FROM information_schema.tables
WHERE table_schema = DATABASE()
  AND table_name = 'employees';

本番データへいきなり入れず、ステージングまたは検証用レコードで1件だけ試します。MySQLでROLLBACKを保護として扱えるのは、InnoDBなどのトランザクション対応エンジンで、まだCOMMITしていない変更に限ります。MyISAMなど非トランザクションテーブルへの変更はROLLBACKできません。ほかのDBMSでもトランザクション条件を確認してください。DDLを同じ試験へ混ぜると、MySQLやOracleなどで暗黙コミットが関係するため避けます。

  1. PostgreSQL・SQLiteはBEGIN、MySQLは対象がInnoDBなどのトランザクション対応エンジンだと確認してからSTART TRANSACTION、SQL ServerはBEGIN TRANSACTIONで開始します。OracleはDML後に明示コミットするまでロールバックできます。
  2. 一意に識別できる検証レコードを1件だけINSERTします。
  3. 主キー、日付列、必要なら時刻表示をSELECTします。
  4. 想定と違えばROLLBACKします。MySQLではトランザクション対応エンジンに限る点を再確認します。
  5. 本番処理では更新件数と保存値を確認してからCOMMITします。

コミット後の取り消しは単純なROLLBACKではできません。削除・訂正が必要なら、バックアップと監査要件を確認し、主キーで対象を限定した別トランザクションとして実施します。この記事では誤って広範囲を消す危険があるため、主キー条件のないDELETE例は掲載しません。

エラーと失敗分岐

症状主な原因安全な確認
変換エラーDBMS非対応構文、曖昧形式DBMS名を確認し、DB別の例へ戻す
月日が逆MDY/DMY、二桁年ISO形式と型付きパラメーターへ統一
無効日付が入ったMySQL SQLモード、SQLite型検査なし設定、警告、制約、アプリ検証を確認
時刻が残るOracle DATE、datetime系列時刻まで明示表示して確認
NULL制約違反必須列へNULL入力を直す。制約を即座に外さない
1日ずれるUTC/ローカル/セッション差業務タイムゾーンを明示
アプリでは失敗する文字列連結、ドライバー型不一致パラメーター型とドライバー資料を確認

エラー時はメッセージ全文、DBMSと版、列定義、実際に送ったパラメーター型、セッション設定を記録します。INSERTを通す目的でSQLモード、NLS設定、DATEFORMAT、列制約を広く変更すると、別処理まで変わります。まず入力とSQLを正し、設定変更は影響調査を伴う別作業にします。

よくある質問

日付は必ずシングルクォートで囲みますか?

文字列表現はシングルクォートで囲みますが、PostgreSQL、MySQL、OracleのDATE '2026-07-12'は、DATEキーワードと引用文字列を組み合わせた型付きリテラルです。アプリではSQLへ引用符を組み立てず、パラメーターAPIへ日付型を渡します。

2026-7-2でも保存できますか?

受理するDBMSはありますが、移植性と入力検証のため2026-07-02へ統一してください。「受理される」と「推奨形式」は別です。二桁年は世紀の解釈も生むため使いません。

DATE列に時刻を入れるとどうなりますか?

PostgreSQL、MySQL、SQL ServerのDATEでは時刻を保持しませんが、変換時の切り捨てまたはエラー挙動をDBMSごとに確認します。OracleのDATEは時刻を保持します。時刻やタイムゾーンが要件なら、DATEではなく適切なTIMESTAMP系の型を設計してください。

CSVから大量INSERTするときも同じですか?

形式の原則は同じですが、製品ごとのバルクロード機能には日付書式、エラー行、トランザクション、文字コードの設定があります。最初に少量で検証し、拒否行と警告を保存し、全件件数と日付範囲を照合してください。1行ずつSQL文字列を連結する方法は避けます。

まとめ

手書きSQLではPostgreSQL・MySQL・OracleのDATE 'YYYY-MM-DD'、SQL ServerのISO 8601文字列を使い、アプリでは型付きパラメーターを使います。NULL、DEFAULT、列省略は別の意味です。Oracle DATEの時刻、MySQLのSQLモードとストレージエンジン、SQLiteの動的型、現在日のタイムゾーンを区別してください。MySQLではInnoDBなどのトランザクション対応エンジンだと確認したうえで、1件をトランザクション内で検証してからコミットします。

公式資料

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次