「データベースに登録日時を自動で記録したい」
「更新時刻を正確に管理したい」
「タイムゾーンの扱いがよく分からない…」
データベースを扱っていると、現在時刻の記録は避けて通れない重要な機能ですよね。
ユーザーの登録日時、注文の受付時刻、ログの記録時間など、あらゆる場面で「いつ」という情報が必要になります。でも、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は、適切な関数とデータ型を選ぶことが重要です。
押さえるべきポイント:
- 基本の関数を使い分ける
- 通常:NOW() または CURRENT_TIMESTAMP
- 日付のみ:CURRENT_DATE
- リアルタイム:CLOCK_TIMESTAMP()
- データ型は用途に応じて
- 推奨:TIMESTAMPTZ(タイムゾーン付き)
- 日付のみ:DATE
- ローカル時刻:TIMESTAMP
- 自動化で楽をする
- DEFAULT NOW() で自動設定
- トリガーで updated_at を自動更新
- 履歴テーブルで変更を追跡
- パフォーマンスを意識
- 適切なインデックス設定
- パーティショニングの活用
- タイムゾーン変換の最小化
- エラーを防ぐ
- タイムゾーンの一貫性
- NULL制約の適切な設定
- フォーマットの統一
時刻管理は、あらゆるシステムの基盤となる重要な要素です。
この記事で紹介した方法を参考に、正確で効率的な時刻管理を実装してください。適切な時刻記録で、信頼性の高いデータベースシステムを構築しましょう!
コメント