Tutorial
SQL でタイムスタンプを変換する方法
はじめに
SQL データベースでタイムスタンプを操作することは、開発者と DBA にとっての基本的なスキルです。このガイドでは、主要なデータベース プラットフォームにわたる変換機能、パフォーマンスに関する考慮事項、および一般的な落とし穴について説明します。
クイックリファレンス
データベース別の変換関数
MySQL
-- Convert Unix timestamp to DATETIME
SELECT
id,
UNIX_TIMESTAMP(created_at) AS converted_date
FROM events;
-- Convert DATETIME to Unix timestamp
SELECT
id,
UNIX_TIMESTAMP(created_at) AS timestamp_value
FROM events;
-- Current timestamp in Unix format
SELECT UNIX_TIMESTAMP(NOW()) AS current_unix;
重要: MySQL TIMESTAMP には 2038 問題があります (範囲: 1970-01-01 00:00:00 ~ 2038-01-19 03:14:07)。この範囲を超える日付には DATETIME または BIGINT を使用してください。
PostgreSQL
-- Convert Unix timestamp to TIMESTAMP WITH TIME ZONE
SELECT
id,
to_timestamp(event_timestamp) AS converted_date
FROM events;
-- Convert timestamp to TIMESTAMPTZ with timezone
SELECT
id,
event_timestamp AT TIME ZONE 'America/New_York' AS eastern_time
FROM events;
-- Current timestamp in Unix format
SELECT EXTRACT(EPOCH FROM NOW()) AS current_unix;
ヒント: PostgreSQL TIMESTAMP WITH TIME ZONE はタイムゾーンを認識します。タイムゾーンをサポートする変換には
AT TIME ZONEを使用します。
SQL サーバー (T-SQL)
-- Convert Unix timestamp to DATETIME2 (higher precision)
SELECT
id,
DATEADD(s, 1970-01-01 00:00:00, UNIX_TIMESTAMP(timestamp_column)) AS converted_date
FROM events;
-- Convert DATETIME2 to Unix timestamp
SELECT
id,
DATEDIFF(s, 1970-01-01 00:00:00, GETDATE()) AS unix_seconds
FROM events;
-- Current timestamp in Unix format
SELECT DATEDIFF(s, 1970-01-01 00:00:00, GETUTCDATE()) AS current_unix;
注: DATETIME2 では、精度を高めるために秒の小数部 (小数点以下 3 桁) が提供されます。正確な計算を行うには、DATEDIFF/DATEADD の組み合わせを使用します。
SQLite
-- Convert Unix timestamp to DATETIME
SELECT
id,
datetime(timestamp_column, 'unixepoch') AS converted_date
FROM events;
-- Current timestamp in Unix format
SELECT strftime('%s', 'now') AS current_unix;
注意: SQLite はデフォルトで Unix エポックを使用します。カスタム形式には
strftime修飾子を使用します。
オラクル
-- Convert Unix timestamp to DATE
SELECT
id,
TO_DATE('1970-01-01', 'YYYY-MM-DD') AS converted_date
FROM events;
-- Current timestamp
SELECT TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS') AS current_timestamp
FROM DUAL;
重要: Oracle TO_DATE 形式では大文字と小文字が区別されます。常に大文字の形式指定子 (YYYY、MM、DD など) を使用してください。
実用的な例
例 1: 変換して表示する
-- MySQL: Convert and format in single query
SELECT
id,
event_name,
DATE_FORMAT(UNIX_TIMESTAMP(created_at), '%Y-%m-%d %H:%i') AS formatted_date,
UNIX_TIMESTAMP(created_at) AS unix_value
FROM events
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
ORDER BY created_at DESC;
例 2: 日付範囲によるフィルター
-- PostgreSQL: Efficient date range filtering with timestamp
SELECT
id,
event_name,
event_timestamp AT TIME ZONE 'UTC' AS utc_time,
TO_CHAR(event_timestamp AT TIME ZONE 'UTC', 'YYYY-MM-DD HH24:MI') AS formatted_date
FROM events
WHERE event_timestamp >= EXTRACT(EPOCH FROM (NOW() - INTERVAL '7 days'))
AND event_timestamp < EXTRACT(EPOCH FROM NOW())
ORDER BY event_timestamp DESC;
例 3: 複数の形式のバッチ変換
-- SQL Server: Provide multiple timestamp formats in one query
SELECT
id,
event_name,
event_timestamp,
CONVERT(VARCHAR, event_timestamp, 120) AS iso8601, -- Truncate to 120 chars
CONVERT(DATETIME2, event_timestamp) AS datetime2,
CONVERT(DATETIME, event_timestamp) AS readable_date,
YEAR(event_timestamp) AS year,
MONTH(event_timestamp) AS month,
DAY(event_timestamp) AS day
FROM events
WHERE event_timestamp IS NOT NULL;
例 4: 時差の計算
-- MySQL: Calculate time difference in seconds
SELECT
id1, id2,
timestamp1, timestamp2,
TIMESTAMPDIFF(SECOND, timestamp2, timestamp1) AS diff_seconds,
TIMESTAMPDIFF(DAY, timestamp2, timestamp1) AS diff_days,
SEC_TO_TIME(TIMESTAMPDIFF(SECOND, timestamp2, timestamp1)) AS time_diff
FROM events
WHERE id IN (1, 2);
パフォーマンスの最適化
インデックス戦略
タイムスタンプ列のプライマリインデックス
ベスト プラクティス: 範囲クエリおよび並べ替え操作では、常にタイムスタンプ列にプライマリ インデックスを作成します。
-- MySQL: Primary index for range queries
CREATE TABLE events (
id INT PRIMARY KEY,
event_name VARCHAR(255),
event_timestamp TIMESTAMP,
INDEX idx_timestamp (event_timestamp)
);
-- PostgreSQL: Partial index for range queries
CREATE TABLE events (
id BIGSERIAL PRIMARY KEY,
event_name VARCHAR(255),
event_timestamp TIMESTAMPTZ,
INDEX idx_timestamp_range (event_timestamp)
);
-- SQL Server: Include timestamp in covering index
CREATE NONCLUSTERED INDEX idx_event_timestamp
ON events (event_timestamp DESC);
機能インデックス
ベスト プラクティス: 日付グループ化クエリの計算列 (年、月、日) に関数インデックスを作成します。
-- MySQL: Functional index for date-based grouping
CREATE TABLE events (
id INT PRIMARY KEY,
event_name VARCHAR(255),
event_timestamp TIMESTAMP,
INDEX idx_year (YEAR(event_timestamp)),
INDEX idx_month (MONTH(event_timestamp)),
INDEX idx_day (DAY(event_timestamp))
);
-- PostgreSQL: Generated column for automatic year/month/day
CREATE TABLE events (
id BIGSERIAL PRIMARY KEY,
event_name VARCHAR(255),
event_timestamp TIMESTAMPTZ,
event_year INTEGER GENERATED ALWAYS AS (EXTRACT(YEAR FROM event_timestamp)) STORED,
event_month INTEGER GENERATED ALWAYS AS (EXTRACT(MONTH FROM event_timestamp)) STORED
);
クエリの最適化
列での関数呼び出しを避ける
よくある間違い: インデックス付き列で YEAR()、MONTH()、DAY() などの関数を使用すると、インデックスが使用できなくなります。
-- BAD: Function call prevents index usage
SELECT id, event_name
FROM events
WHERE YEAR(event_timestamp) = 2024
AND MONTH(event_timestamp) = 6;
-- GOOD: Compare literal values to use index
SELECT id, event_name
FROM events
WHERE event_timestamp >= '2024-01-01 00:00:00'
AND event_timestamp < '2024-07-01 00:00:00';
SARGABLE 可能なパラメータを使用する
MySQL ヒント: MySQL 8.0.18 以降、プリペアド ステートメントで SARGABLE パラメータを使用すると、インデックスを使用できるようになります。
-- MySQL: Create prepared statement for parameterized queries
PREPARE stmt FROM 'SELECT * FROM events WHERE event_timestamp >= ? AND event_timestamp < ?';
-- Execute with parameters (efficient)
EXECUTE stmt USING @start_ts, @end_ts;
結果セットを制限する
ベスト プラクティス: 範囲クエリでは常に LIMIT を使用して、必要なデータのみを返します。
-- MySQL: Paginate large datasets
SELECT id, event_name, event_timestamp
FROM events
WHERE event_timestamp >= UNIX_TIMESTAMP('2025-01-01')
ORDER BY event_timestamp ASC
LIMIT 1000;
-- PostgreSQL: Use cursor-based pagination for large result sets
DECLARE cursor CURSOR FOR
SELECT id, event_name, event_timestamp
FROM events
WHERE event_timestamp >= '2025-01-01 00:00:00 UTC'
ORDER BY event_timestamp ASC;
よくある落とし穴
タイムゾーンの処理
MySQL タイムゾーン関数
重要: MySQL TIMESTAMP はタイムゾーン情報を保存しません。タイムゾーンを認識するストレージには TIMESTAMP WITH TIME ZONE を使用するか、別のタイムゾーン列を持つ DATETIME を使用します。
-- Convert to specific timezone
SELECT
id,
event_name,
event_timestamp AT TIME ZONE 'America/Los_Angeles' AS la_time,
event_timestamp AT TIME ZONE 'America/New_York' AS ny_time
FROM events;
-- Get current time in specific timezone
SELECT NOW() AS la_current, CONVERT_TZ(UTC, 'America/New_York') AS ny_current;
代替案: マルチタイムゾーン アプリケーションの場合は、UTC タイムスタンプと別のタイムゾーン列を保存します。
データの整合性
タイムスタンプの一貫性を確保する
-- MySQL: Add CHECK constraint for reasonable timestamp range
CREATE TABLE events (
id INT PRIMARY KEY,
event_timestamp TIMESTAMP NOT NULL,
CHECK (event_timestamp BETWEEN
UNIX_TIMESTAMP('1970-01-01') AND
UNIX_TIMESTAMP('2038-01-19')
)
);
-- PostgreSQL: Use EXCLUDE constraint to prevent invalid data
CREATE TABLE events (
id BIGSERIAL PRIMARY KEY,
event_timestamp TIMESTAMPTZ NOT NULL,
EXCLUDE (
event_timestamp < EXTRACT(EPOCH FROM TIMESTAMP '1970-01-01')
OR event_timestamp > EXTRACT(EPOCH FROM TIMESTAMP '2038-01-19')
)
);
移行戦略
増分移行
戦略: 大きなテーブルの場合は、長時間実行されるトランザクションを避けるために、タイムスタンプ ベースの WHERE 句を使用してバッチで移行します。
-- Process 1000 rows at a time, ordered by timestamp
UPDATE events
SET last_processed = 1
WHERE event_timestamp < (
SELECT event_timestamp
FROM events
WHERE last_processed = 0
ORDER BY event_timestamp ASC
LIMIT 1000
);
-- Continue until all rows processed
-- Repeat until all events marked as processed
高度なパターン
エポックタイムスタンプの処理
データベース固有のパターン
MySQL パターン
マイクロ秒精度の Unix タイムスタンプ
-- MySQL: Using BIGINT for millisecond timestamps
CREATE TABLE high_precision_events (
id BIGINT PRIMARY KEY,
event_timestamp BIGINT, -- Milliseconds since epoch
event_micros INT(3), -- Microseconds (0-999)
INDEX idx_timestamp (event_timestamp DESC)
);
-- Convert millisecond timestamp to human-readable format
SELECT
id,
FROM_UNIXTIME(event_timestamp / 1000) AS seconds,
DATE_FORMAT(FROM_UNIXTIME(event_timestamp / 1000), '%Y-%m-%d %H:%i:%s') AS formatted
FROM high_precision_events;
PostgreSQL パターン
日付コンポーネントに EXTRACT を使用する
-- PostgreSQL: Extract date components for filtering
SELECT
id,
event_name,
EXTRACT(YEAR FROM event_timestamp) AS year,
EXTRACT(MONTH FROM event_timestamp) AS month,
EXTRACT(DAY FROM event_timestamp) AS day,
DATE_TRUNC('day', event_timestamp) AS date_only
FROM events
WHERE event_name = 'Daily Backup';
ベスト プラクティスの概要
パフォーマンスチェックリスト
✅ Use appropriate data types (TIMESTAMP vs DATETIME)
✅ Create indexes on timestamp columns
✅ Use SARGABLE-able parameters when possible
✅ Limit result sets with pagination
✅ Avoid function calls on indexed columns
✅ Use WHERE clauses on timestamp for range queries
✅ Consider generated columns for date filtering
✅ Validate timestamp ranges on input
✅ Handle timezones explicitly (don't rely on implicit conversion)
✅ Use CHECK/EXCLUDE constraints for data integrity
❌ Don't store both timestamp and datetime for same data
❌ Don't use VARCHAR for timestamp columns
❌ Don't convert timestamp to string for every query
大量のタイムスタンプ アプリケーション (100 万行を超える) の場合、適切なインデックス作成とクエリ パターンにより、クエリ時間を 50 ~ 80% 削減できます。
関連ツール
- タイムスタンプ形式コンバータ - 複数のタイムスタンプ形式間の変換
- Unix タイムスタンプ コンバータ - Unix タイムスタンプを日付に変換します
- 現在のタイムスタンプ - 現在のタイムスタンプを複数の形式で取得します
- タイムスタンプバリデーター - タイムスタンプの形式と範囲を検証します。