PostgreSQLで現在時刻をINSERTする完全ガイド|タイムスタンプの使い方徹底解説

データベース・SQL

「データベースに登録日時を自動で記録したい」
「更新時刻を正確に管理したい」
「タイムゾーンの扱いがよく分からない…」

データベースを扱っていると、現在時刻の記録は避けて通れない重要な機能ですよね。

ユーザーの登録日時、注文の受付時刻、ログの記録時間など、あらゆる場面で「いつ」という情報が必要になります。でも、PostgreSQLには現在時刻を扱う関数がたくさんあって、どれを使えばいいのか迷ってしまうことも多いでしょう。

NOW()、CURRENT_TIMESTAMP、CURRENT_DATE…一体何が違うの?タイムゾーンはどう扱えばいいの?そんな疑問を持つ方も多いはずです。

この記事では、PostgreSQLで現在時刻をINSERTするあらゆる方法を、実際のビジネスシーンを想定した具体例とともに、初心者の方でも理解できるように詳しく解説していきます。

正確な時刻管理で、信頼性の高いデータベースを構築しましょう!

スポンサーリンク

PostgreSQLの日時型を理解する

4つの主要な日時データ型

PostgreSQLには、時刻を扱うデータ型が複数あります。それぞれの特徴を理解しましょう。

主要な日時型:

データ型説明形式例用途
DATE日付のみ2025-01-20誕生日、記念日
TIME時刻のみ14:30:45営業時間、アラーム
TIMESTAMP日付と時刻2025-01-20 14:30:45ログ、履歴(タイムゾーンなし)
TIMESTAMPTZ日付と時刻(タイムゾーン付き)2025-01-20 14:30:45+09国際的なシステム

どれを選ぶべきか:

  • ほとんどの場合:TIMESTAMPTZ(タイムゾーン付き)
  • 日付だけで十分:DATE
  • ローカル時刻のみ:TIMESTAMP(タイムゾーンなし)

タイムゾーンの扱いで悩むことが多いので、基本的にはTIMESTAMPTZを使うのがおすすめです。

タイムゾーンの重要性

タイムゾーンを正しく扱わないと、思わぬバグの原因になります。

TIMESTAMPとTIMESTAMPTZの違い:

-- テーブル作成
CREATE TABLE time_test (
    id SERIAL PRIMARY KEY,
    without_tz TIMESTAMP,
    with_tz TIMESTAMPTZ
);

-- 同じ値を挿入
INSERT INTO time_test (without_tz, with_tz) VALUES 
    ('2025-01-20 14:30:00', '2025-01-20 14:30:00');

-- タイムゾーンを変更して確認
SET timezone = 'America/New_York';
SELECT * FROM time_test;
-- without_tz: 2025-01-20 14:30:00 (変わらない)
-- with_tz: 2025-01-20 00:30:00-05 (自動変換される)

SET timezone = 'Asia/Tokyo';
SELECT * FROM time_test;
-- without_tz: 2025-01-20 14:30:00 (変わらない)
-- with_tz: 2025-01-20 14:30:00+09 (元に戻る)

TIMESTAMPTZは常にUTCで保存され、表示時に自動変換されます。

現在時刻を取得する関数

NOW()関数 – 最も汎用的

NOW()は最も頻繁に使われる関数です。

-- 現在のタイムスタンプを取得
SELECT NOW();
-- 結果: 2025-01-20 14:30:45.123456+09

-- INSERTで使用
INSERT INTO users (name, created_at) 
VALUES ('田中太郎', NOW());

-- 日付部分だけ取得
SELECT NOW()::DATE;
-- 結果: 2025-01-20

-- 時刻部分だけ取得
SELECT NOW()::TIME;
-- 結果: 14:30:45.123456

NOW()の特徴:

  • トランザクション開始時の時刻を返す
  • トランザクション内では同じ値
  • 最も使いやすく汎用的

CURRENT_TIMESTAMP – 標準SQL準拠

CURRENT_TIMESTAMPは、NOW()とほぼ同じ動作をします。

-- 基本的な使用
SELECT CURRENT_TIMESTAMP;
-- 結果: 2025-01-20 14:30:45.123456+09

-- 精度を指定(小数点以下の桁数)
SELECT CURRENT_TIMESTAMP(0);  -- 秒まで
-- 結果: 2025-01-20 14:30:45+09

SELECT CURRENT_TIMESTAMP(3);  -- ミリ秒まで
-- 結果: 2025-01-20 14:30:45.123+09

-- INSERTで使用
INSERT INTO logs (message, logged_at) 
VALUES ('エラーが発生しました', CURRENT_TIMESTAMP);

標準SQLに準拠しているので、他のデータベースとの互換性を重視する場合はこちらを使います。

CURRENT_DATE と CURRENT_TIME

日付や時刻だけが必要な場合の関数です。

-- 現在の日付(時刻なし)
SELECT CURRENT_DATE;
-- 結果: 2025-01-20

-- 現在の時刻(日付なし)
SELECT CURRENT_TIME;
-- 結果: 14:30:45.123456+09

-- 実用例:今日の売上を取得
SELECT * FROM sales 
WHERE sale_date = CURRENT_DATE;

-- 営業時間内かチェック
SELECT CURRENT_TIME BETWEEN '09:00:00' AND '18:00:00' AS is_open;

CLOCK_TIMESTAMP() – リアルタイム取得

トランザクション中でも現在時刻を取得したい場合に使います。

-- トランザクション内での違い
BEGIN;

SELECT NOW() AS now1;  
-- 結果: 2025-01-20 14:30:45

-- 3秒待機
SELECT pg_sleep(3);

SELECT NOW() AS now2;  
-- 結果: 2025-01-20 14:30:45 (同じ値)

SELECT CLOCK_TIMESTAMP() AS clock;  
-- 結果: 2025-01-20 14:30:48 (3秒後の時刻)

COMMIT;

使い分け:

  • 通常のタイムスタンプ:NOW()
  • 処理時間の計測:CLOCK_TIMESTAMP()
  • バッチ処理の進捗記録:CLOCK_TIMESTAMP()

INSERT文での基本的な使い方

直接値を指定してINSERT

最も基本的な現在時刻のINSERT方法です。

-- ユーザーテーブルの作成
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL,
    created_at TIMESTAMPTZ NOT NULL,
    updated_at TIMESTAMPTZ NOT NULL
);

-- 現在時刻を直接指定してINSERT
INSERT INTO users (username, email, created_at, updated_at)
VALUES ('tanaka', 'tanaka@example.com', NOW(), NOW());

-- 複数行を一度にINSERT
INSERT INTO users (username, email, created_at, updated_at)
VALUES 
    ('suzuki', 'suzuki@example.com', NOW(), NOW()),
    ('sato', 'sato@example.com', NOW(), NOW()),
    ('yamada', 'yamada@example.com', NOW(), NOW());

デフォルト値として設定

毎回NOW()を書かなくても自動で現在時刻が入るようにできます。

-- デフォルト値付きのテーブル作成
CREATE TABLE posts (
    id SERIAL PRIMARY KEY,
    title VARCHAR(200) NOT NULL,
    content TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- created_atとupdated_atを省略してINSERT
INSERT INTO posts (title, content)
VALUES ('最初の投稿', 'これはテスト投稿です');

-- 確認
SELECT * FROM posts;
-- created_atとupdated_atに自動で現在時刻が入る

既存テーブルにデフォルト値を追加:

ALTER TABLE users 
ALTER COLUMN created_at SET DEFAULT NOW();

RETURNING句で挿入値を確認

INSERTした時刻を確認する便利な方法です。

-- 挿入した値をすぐに確認
INSERT INTO posts (title, content)
VALUES ('新しい記事', '内容です')
RETURNING id, title, created_at;

-- 結果:
-- id | title      | created_at
-- ---|------------|-------------------------
-- 2  | 新しい記事 | 2025-01-20 14:30:45+09

-- 複数の値を返す
INSERT INTO users (username, email, created_at, updated_at)
VALUES ('newuser', 'new@example.com', NOW(), NOW())
RETURNING *, NOW() - created_at AS elapsed_time;

RETURNINGを使えば、INSERTと同時に結果を取得できて効率的です。

デフォルト値とトリガーの活用

DEFAULT制約の設定

テーブル作成時にDEFAULT制約を設定する詳細な方法です。

-- 様々なDEFAULT設定の例
CREATE TABLE articles (
    id SERIAL PRIMARY KEY,
    title VARCHAR(200) NOT NULL,
    content TEXT,
    status VARCHAR(20) DEFAULT 'draft',

    -- 作成日時(変更不可)
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    -- 更新日時(自動更新したい)
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    -- 公開予定日時(1週間後をデフォルト)
    publish_at TIMESTAMPTZ DEFAULT NOW() + INTERVAL '7 days',

    -- 有効期限(1年後)
    expires_at TIMESTAMPTZ DEFAULT NOW() + INTERVAL '1 year'
);

-- データ挿入(日時関連は全て自動設定)
INSERT INTO articles (title, content)
VALUES ('自動設定のテスト', 'デフォルト値が設定されます');

-- 確認
SELECT 
    title,
    created_at,
    publish_at,
    expires_at
FROM articles;

更新時刻を自動更新するトリガー

updated_atを自動的に更新するトリガーの実装です。

-- 更新時刻を自動更新する関数
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- トリガーの作成
CREATE TRIGGER update_articles_updated_at
    BEFORE UPDATE ON articles
    FOR EACH ROW
    EXECUTE FUNCTION update_updated_at_column();

-- テスト
UPDATE articles 
SET title = '更新されたタイトル'
WHERE id = 1;

-- updated_atが自動的に更新される
SELECT id, title, created_at, updated_at FROM articles WHERE id = 1;

汎用的なトリガー関数:

-- どのテーブルでも使える汎用関数
CREATE OR REPLACE FUNCTION trigger_set_timestamp()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 複数のテーブルに適用
CREATE TRIGGER set_timestamp
    BEFORE UPDATE ON users
    FOR EACH ROW
    EXECUTE FUNCTION trigger_set_timestamp();

CREATE TRIGGER set_timestamp
    BEFORE UPDATE ON posts
    FOR EACH ROW
    EXECUTE FUNCTION trigger_set_timestamp();

履歴テーブルの自動作成

変更履歴を自動記録するトリガーです。

-- 履歴テーブル
CREATE TABLE users_history (
    history_id SERIAL PRIMARY KEY,
    user_id INT,
    username VARCHAR(50),
    email VARCHAR(100),
    action VARCHAR(10),
    changed_at TIMESTAMPTZ DEFAULT NOW(),
    changed_by VARCHAR(50) DEFAULT CURRENT_USER
);

-- 履歴記録トリガー
CREATE OR REPLACE FUNCTION log_user_changes()
RETURNS TRIGGER AS $$
BEGIN
    IF TG_OP = 'UPDATE' THEN
        INSERT INTO users_history (user_id, username, email, action)
        VALUES (OLD.id, OLD.username, OLD.email, 'UPDATE');
    ELSIF TG_OP = 'DELETE' THEN
        INSERT INTO users_history (user_id, username, email, action)
        VALUES (OLD.id, OLD.username, OLD.email, 'DELETE');
    END IF;
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER user_audit
    AFTER UPDATE OR DELETE ON users
    FOR EACH ROW
    EXECUTE FUNCTION log_user_changes();

タイムゾーンの扱い方

タイムゾーンの設定と確認

PostgreSQLでのタイムゾーン管理方法です。

-- 現在のタイムゾーン設定を確認
SHOW timezone;
-- 結果: Asia/Tokyo

-- 利用可能なタイムゾーン一覧
SELECT name FROM pg_timezone_names 
WHERE name LIKE '%Tokyo%' OR name LIKE '%York%';

-- セッション単位でタイムゾーンを変更
SET timezone = 'America/New_York';

-- データベース全体のデフォルトを変更(要管理者権限)
ALTER DATABASE mydb SET timezone = 'Asia/Tokyo';

-- ユーザー単位で設定
ALTER USER myuser SET timezone = 'Europe/London';

AT TIME ZONEでの変換

異なるタイムゾーン間での時刻変換です。

-- 現在時刻を異なるタイムゾーンで表示
SELECT 
    NOW() AS tokyo_time,
    NOW() AT TIME ZONE 'America/New_York' AS ny_time,
    NOW() AT TIME ZONE 'Europe/London' AS london_time,
    NOW() AT TIME ZONE 'UTC' AS utc_time;

-- 特定の時刻を別のタイムゾーンに変換
SELECT 
    '2025-01-20 15:00:00+09'::TIMESTAMPTZ AS original,
    '2025-01-20 15:00:00+09'::TIMESTAMPTZ AT TIME ZONE 'America/Los_Angeles' AS la_time;

-- 実用例:各国のオフィス営業時間をチェック
WITH office_hours AS (
    SELECT 
        'Tokyo' AS office,
        '09:00:00'::TIME AS open_time,
        '18:00:00'::TIME AS close_time,
        'Asia/Tokyo' AS timezone
    UNION ALL
    SELECT 'New York', '09:00:00'::TIME, '17:00:00'::TIME, 'America/New_York'
    UNION ALL
    SELECT 'London', '09:00:00'::TIME, '17:30:00'::TIME, 'Europe/London'
)
SELECT 
    office,
    NOW() AT TIME ZONE timezone AS local_time,
    CASE 
        WHEN (NOW() AT TIME ZONE timezone)::TIME BETWEEN open_time AND close_time 
        THEN '営業中'
        ELSE '営業時間外'
    END AS status
FROM office_hours;

UTCでの統一管理

国際的なシステムではUTC管理が推奨されます。

-- UTCで保存、表示時にローカル時間に変換
CREATE TABLE global_events (
    id SERIAL PRIMARY KEY,
    event_name VARCHAR(100),
    event_time_utc TIMESTAMPTZ NOT NULL,
    local_timezone VARCHAR(50) NOT NULL
);

-- UTCで保存
INSERT INTO global_events (event_name, event_time_utc, local_timezone)
VALUES 
    ('東京ミーティング', NOW() AT TIME ZONE 'UTC', 'Asia/Tokyo'),
    ('NYカンファレンス', NOW() AT TIME ZONE 'UTC', 'America/New_York');

-- ローカル時間で表示
SELECT 
    event_name,
    event_time_utc AS utc_time,
    event_time_utc AT TIME ZONE local_timezone AS local_time,
    local_timezone
FROM global_events;

実践的な使用例

ログテーブルの実装

アクセスログや操作ログの記録です。

-- アクセスログテーブル
CREATE TABLE access_logs (
    id BIGSERIAL PRIMARY KEY,
    user_id INT,
    ip_address INET,
    user_agent TEXT,
    endpoint VARCHAR(200),
    method VARCHAR(10),
    status_code INT,
    response_time_ms INT,
    accessed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    -- インデックス
    INDEX idx_accessed_at (accessed_at),
    INDEX idx_user_id_accessed (user_id, accessed_at)
);

-- パーティショニング(月単位)
CREATE TABLE access_logs_2025_01 PARTITION OF access_logs
    FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

-- ログの挿入
INSERT INTO access_logs (user_id, ip_address, endpoint, method, status_code, response_time_ms)
VALUES 
    (123, '192.168.1.1', '/api/users', 'GET', 200, 45),
    (124, '192.168.1.2', '/api/posts', 'POST', 201, 120);

-- 時間帯別アクセス分析
SELECT 
    DATE_TRUNC('hour', accessed_at) AS hour,
    COUNT(*) AS access_count,
    AVG(response_time_ms) AS avg_response_time
FROM access_logs
WHERE accessed_at >= NOW() - INTERVAL '24 hours'
GROUP BY DATE_TRUNC('hour', accessed_at)
ORDER BY hour;

予約システムの実装

時刻管理が重要な予約システムの例です。

-- 予約テーブル
CREATE TABLE reservations (
    id SERIAL PRIMARY KEY,
    customer_name VARCHAR(100) NOT NULL,
    reservation_date DATE NOT NULL,
    start_time TIME NOT NULL,
    end_time TIME NOT NULL,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    confirmed_at TIMESTAMPTZ,
    cancelled_at TIMESTAMPTZ,

    -- 制約:終了時刻は開始時刻より後
    CONSTRAINT valid_time_range CHECK (end_time > start_time),
    -- 制約:予約は未来の日付のみ
    CONSTRAINT future_reservation CHECK (reservation_date >= CURRENT_DATE)
);

-- 予約の作成
INSERT INTO reservations (customer_name, reservation_date, start_time, end_time)
VALUES ('田中太郎', CURRENT_DATE + 1, '14:00', '15:00');

-- 予約の確認処理
UPDATE reservations 
SET confirmed_at = NOW()
WHERE id = 1 AND confirmed_at IS NULL;

-- 本日の予約一覧
SELECT 
    customer_name,
    start_time,
    end_time,
    CASE 
        WHEN confirmed_at IS NOT NULL THEN '確認済'
        WHEN cancelled_at IS NOT NULL THEN 'キャンセル'
        ELSE '未確認'
    END AS status
FROM reservations
WHERE reservation_date = CURRENT_DATE
ORDER BY start_time;

有効期限管理

クーポンや会員資格の有効期限管理です。

-- クーポンテーブル
CREATE TABLE coupons (
    id SERIAL PRIMARY KEY,
    code VARCHAR(20) UNIQUE NOT NULL,
    discount_percent INT NOT NULL,
    issued_at TIMESTAMPTZ DEFAULT NOW(),
    valid_from TIMESTAMPTZ DEFAULT NOW(),
    valid_until TIMESTAMPTZ DEFAULT NOW() + INTERVAL '30 days',
    used_at TIMESTAMPTZ,
    user_id INT
);

-- 有効なクーポンを取得するビュー
CREATE VIEW active_coupons AS
SELECT *
FROM coupons
WHERE NOW() BETWEEN valid_from AND valid_until
  AND used_at IS NULL;

-- クーポン使用処理
UPDATE coupons 
SET used_at = NOW(), user_id = 123
WHERE code = 'SAVE20' 
  AND used_at IS NULL
  AND NOW() BETWEEN valid_from AND valid_until;

-- 期限切れ間近のクーポン(3日以内)
SELECT 
    code,
    discount_percent,
    valid_until,
    valid_until - NOW() AS time_remaining
FROM coupons
WHERE used_at IS NULL
  AND valid_until BETWEEN NOW() AND NOW() + INTERVAL '3 days'
ORDER BY valid_until;

パフォーマンスの最適化

インデックス戦略

時刻カラムに対する効果的なインデックス設定です。

-- 基本的なインデックス
CREATE INDEX idx_created_at ON users(created_at);
CREATE INDEX idx_created_at_desc ON users(created_at DESC);

-- 複合インデックス
CREATE INDEX idx_user_created ON posts(user_id, created_at DESC);

-- 部分インデックス(最近のデータのみ)
CREATE INDEX idx_recent_logs ON access_logs(accessed_at)
WHERE accessed_at >= NOW() - INTERVAL '7 days';

-- BRIN インデックス(時系列データに最適)
CREATE INDEX idx_logs_brin ON access_logs 
USING BRIN(accessed_at) WITH (pages_per_range = 128);

-- パフォーマンステスト
EXPLAIN ANALYZE
SELECT * FROM access_logs
WHERE accessed_at >= NOW() - INTERVAL '1 hour';

パーティショニング

大量の時系列データを効率的に管理します。

-- 月単位のパーティションテーブル
CREATE TABLE events (
    id BIGSERIAL,
    event_type VARCHAR(50),
    event_data JSONB,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);

-- 自動パーティション作成関数
CREATE OR REPLACE FUNCTION create_monthly_partition()
RETURNS void AS $$
DECLARE
    start_date DATE;
    end_date DATE;
    partition_name TEXT;
BEGIN
    start_date := DATE_TRUNC('month', NOW());
    end_date := start_date + INTERVAL '1 month';
    partition_name := 'events_' || TO_CHAR(start_date, 'YYYY_MM');

    EXECUTE format('
        CREATE TABLE IF NOT EXISTS %I PARTITION OF events
        FOR VALUES FROM (%L) TO (%L)',
        partition_name, start_date, end_date
    );
END;
$$ LANGUAGE plpgsql;

-- 定期実行(pg_cronなどで月次実行)
SELECT create_monthly_partition();

クエリの最適化

時刻を条件にしたクエリの最適化テクニックです。

-- 非効率なクエリ
SELECT * FROM orders
WHERE DATE(created_at) = '2025-01-20';  -- インデックスが使えない

-- 効率的なクエリ
SELECT * FROM orders
WHERE created_at >= '2025-01-20'::DATE
  AND created_at < '2025-01-20'::DATE + INTERVAL '1 day';

-- 関数インデックスを使う場合
CREATE INDEX idx_created_date ON orders(DATE(created_at));

-- 統計情報の更新
ANALYZE orders;

-- クエリプランの確認
EXPLAIN (ANALYZE, BUFFERS) 
SELECT COUNT(*) FROM orders
WHERE created_at >= NOW() - INTERVAL '7 days';

トラブルシューティング

よくあるエラーと対処法

時刻関連でよく発生するエラーです。

-- エラー: invalid input syntax for type timestamp
-- 原因: 不正な日時フォーマット
-- 解決法: 正しいフォーマットを使用
INSERT INTO events (event_time) VALUES ('2025-01-20 14:30:00');  -- OK
INSERT INTO events (event_time) VALUES ('20/01/2025 14:30');     -- NG

-- エラー: column "created_at" cannot be null
-- 解決法: デフォルト値を設定
ALTER TABLE users ALTER COLUMN created_at SET DEFAULT NOW();

-- エラー: date/time field value out of range
-- 原因: 存在しない日付
-- 解決法: 日付の妥当性をチェック
INSERT INTO events (event_date) VALUES ('2025-02-30');  -- NG(2月30日は存在しない)

タイムゾーンの問題

タイムゾーン関連のトラブル解決です。

-- 問題: 時刻がずれて表示される
-- 確認事項:
SHOW timezone;  -- サーバーのタイムゾーン
SELECT NOW();    -- 現在時刻の確認

-- 解決法1: セッションのタイムゾーン設定
SET timezone = 'Asia/Tokyo';

-- 解決法2: 明示的な変換
SELECT 
    created_at,
    created_at AT TIME ZONE 'Asia/Tokyo' AS japan_time
FROM users;

-- 問題: TIMESTAMPとTIMESTAMPTZの混在
-- 既存のTIMESTAMPをTIMESTAMPTZに変換
ALTER TABLE old_table 
ALTER COLUMN created_at TYPE TIMESTAMPTZ 
USING created_at AT TIME ZONE 'Asia/Tokyo';

パフォーマンスの問題

時刻処理が遅い場合の対策です。

-- 問題: 日付での集計が遅い
-- 改善前
SELECT DATE(created_at), COUNT(*)
FROM large_table
GROUP BY DATE(created_at);

-- 改善後(インデックス利用)
CREATE INDEX idx_created_date ON large_table(DATE(created_at));

-- さらに改善(マテリアライズドビュー)
CREATE MATERIALIZED VIEW daily_stats AS
SELECT 
    DATE(created_at) AS date,
    COUNT(*) AS count,
    MAX(created_at) AS last_update
FROM large_table
GROUP BY DATE(created_at);

CREATE UNIQUE INDEX ON daily_stats(date);

-- 定期更新
REFRESH MATERIALIZED VIEW CONCURRENTLY daily_stats;

よくある質問

Q: NOW()とCURRENT_TIMESTAMPの違いは?

A: 機能的にはほぼ同じです。

NOW()はPostgreSQL固有の関数で、CURRENT_TIMESTAMPは標準SQLです。両方ともトランザクション開始時の時刻を返します。移植性を重視するならCURRENT_TIMESTAMP、簡潔さを重視するならNOW()を使いましょう。

Q: created_atとupdated_atは必ず必要?

A: ほぼすべてのテーブルに追加することをおすすめします。

データの追跡、デバッグ、監査、分析などで非常に役立ちます。ストレージのコストは最小限で、後から「いつ作られたか知りたい」となっても手遅れです。

Q: タイムゾーンはTIMESTAMPTZを使うべき?

A: 基本的にはTIMESTAMPTZを推奨します。

グローバルなアプリケーションはもちろん、ローカルなシステムでも夏時間やタイムゾーン変更に対応できます。特別な理由がない限り、TIMESTAMPTZを使いましょう。

Q: ミリ秒まで記録する必要はある?

A: アプリケーションの要件次第です。

通常の業務システムなら秒単位で十分ですが、ログ分析や高頻度取引では、ミリ秒やマイクロ秒が重要になります。CURRENT_TIMESTAMP(3)でミリ秒精度を指定できます。

Q: 過去や未来の時刻を手動で入れてもいい?

A: はい、可能です。

テストデータの作成や、データ移行、予約システムなどでは必要になります。ただし、監査ログなど改ざん防止が必要な場合は、トリガーで制限することも検討してください。

まとめ

PostgreSQLでの現在時刻のINSERTは、適切な関数とデータ型を選ぶことが重要です。

押さえるべきポイント:

  1. 基本の関数を使い分ける
  • 通常:NOW() または CURRENT_TIMESTAMP
  • 日付のみ:CURRENT_DATE
  • リアルタイム:CLOCK_TIMESTAMP()
  1. データ型は用途に応じて
  • 推奨:TIMESTAMPTZ(タイムゾーン付き)
  • 日付のみ:DATE
  • ローカル時刻:TIMESTAMP
  1. 自動化で楽をする
  • DEFAULT NOW() で自動設定
  • トリガーで updated_at を自動更新
  • 履歴テーブルで変更を追跡
  1. パフォーマンスを意識
  • 適切なインデックス設定
  • パーティショニングの活用
  • タイムゾーン変換の最小化
  1. エラーを防ぐ
  • タイムゾーンの一貫性
  • NULL制約の適切な設定
  • フォーマットの統一

時刻管理は、あらゆるシステムの基盤となる重要な要素です。

この記事で紹介した方法を参考に、正確で効率的な時刻管理を実装してください。適切な時刻記録で、信頼性の高いデータベースシステムを構築しましょう!

コメント

タイトルとURLをコピーしました