Tutorial
Migração de carimbo de data/hora entre bancos de dados: guia completo
Introdução
A migração de dados de carimbo de data/hora entre diferentes sistemas de banco de dados é um desafio comum no desenvolvimento de aplicativos e em projetos de data warehouse. Cada banco de dados tem seus próprios tipos de dados de carimbo de data/hora, funções e práticas recomendadas. Este guia abrangente ensinará como migrar carimbos de data/hora entre MySQL, PostgreSQL, SQLite, Oracle e SQL Server sem perda de dados.
Noções básicas sobre estratégias de armazenamento de carimbo de data/hora
Opção 1: carimbo de data/hora Unix (inteiro)
A abordagem mais portátil é armazenar carimbos de data/hora como segundos da época Unix ou milissegundos como números inteiros.
-- MySQL
CREATE TABLE events (
id INT PRIMARY KEY,
created_at BIGINT NOT NULL -- Unix timestamp in seconds
);
-- PostgreSQL
CREATE TABLE events (
id INT PRIMARY KEY,
created_at BIGINT NOT NULL
);
-- SQLite
CREATE TABLE events (
id INTEGER PRIMARY KEY,
created_at INTEGER NOT NULL
);
Vantagens:
- Formato universal em todos os bancos de dados
- Sem problemas de fuso horário
- Fácil de converter para qualquer formato de exibição
- Eficiente para indexação e comparação
Desvantagens:
- Não é legível por humanos na forma bruta
- Requer conversão para depuração
Opção 2: Cadeia ISO 8601
Armazene carimbos de data/hora como strings formatadas em ISO 8601.
-- MySQL
CREATE TABLE events (
id INT PRIMARY KEY,
created_at VARCHAR(26) NOT NULL -- '2025-01-15T10:30:00.000Z'
);
-- PostgreSQL
CREATE TABLE events (
id INT PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL
);
Vantagens:
- Legível por humanos
- Inclui informações de fuso horário
- Formato padrão
Desvantagens:
- Maior tamanho de armazenamento
- Comparações mais lentas
- A eficiência do índice varia
Opção 3: Tipos de carimbo de data/hora nativos
Usando os tipos de carimbo de data/hora nativos de cada banco de dados.
-- MySQL
CREATE TABLE events (
created_at DATETIME
);
-- PostgreSQL
CREATE TABLE events (
created_at TIMESTAMP WITH TIME ZONE
);
-- SQL Server
CREATE TABLE events (
created_at DATATIME2
);
Vantagens:
- Melhor desempenho para operações de data
- Validação integrada
- Rico ecossistema de funções
Desvantagens:
- Complexidade da migração
- O tratamento do fuso horário varia
Cenários de migração
Cenário 1: MySQL para PostgreSQL
Convertendo de DATETIME do MySQL para TIMESTAMPTZ do PostgreSQL.
-- Source (MySQL)
SELECT id, created_at FROM events;
-- Migration Script (PostgreSQL)
-- Option A: Using TO_TIMESTAMP()
INSERT INTO events (id, created_at)
SELECT id, TO_TIMESTAMP(created_at, 'YYYY-MM-DD HH24:MI:SS')
FROM events_source;
-- Option B: Using Unix timestamp (recommended)
-- First, convert MySQL DATETIME to Unix timestamp
SELECT UNIX_TIMESTAMP(created_at) FROM events;
-- Then insert into PostgreSQL
INSERT INTO events (id, created_at)
SELECT id, TO_TIMESTAMP(unix_ts)
FROM events_source;
Cenário 2: Oracle para SQL Server
Migrando do DATE do Oracle para o DATETIME2 do SQL Server.
-- Source (Oracle)
SELECT hire_date FROM employees;
-- Migration Script (SQL Server)
-- Option A: Direct conversion
INSERT INTO employees (hire_date)
SELECT CAST(hire_date AS DATETIME2)
FROM employees_source;
-- Option B: Using Unix epoch (preferred)
-- Oracle: Get epoch seconds
SELECT (hire_date - DATE '1970-01-01') * 86400 AS epoch_seconds
FROM employees;
-- SQL Server: Convert epoch to datetime
INSERT INTO employees (hire_date)
SELECT DATEADD(SECOND, epoch_seconds, '1970-01-01')
FROM employees_source;
Cenário 3: conversão entre fusos horários
Ao migrar entre bancos de dados com requisitos de fuso horário diferentes.
-- PostgreSQL: Convert UTC to specific timezone
INSERT INTO events (id, created_at)
SELECT id, created_at AT TIME ZONE 'America/New_York'
FROM events_source;
-- MySQL: Convert using CONVERT_TZ
INSERT INTO events (id, created_at)
SELECT id, CONVERT_TZ(created_at, '+00:00', '-05:00')
FROM events_source;
-- SQL Server: Convert using AT TIME ZONE
INSERT INTO events (id, created_at)
SELECT created_at AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time'
FROM events_source;
Melhores práticas
1. Sempre use UTC para armazenamento
-- Store all timestamps in UTC
ALTER TABLE events
ADD COLUMN created_at_utc TIMESTAMP WITH TIME ZONE;
UPDATE events
SET created_at_utc = created_at AT TIME ZONE 'UTC';
-- Drop the old column after verification
ALTER TABLE events DROP COLUMN created_at;
2. Validar antes da migração
-- Check for invalid timestamps
SELECT COUNT(*) as invalid_count
FROM events_source
WHERE created_at IS NULL
OR created_at < '1970-01-01'::timestamp
OR created_at > '2038-01-19'::timestamp; -- 32-bit overflow
3. Use tabelas de teste
-- Create staging table
CREATE TABLE events_staging (LIKE events);
-- Load data
INSERT INTO events_staging
SELECT * FROM events_source;
-- Validate and clean
UPDATE events_staging
SET created_at = NOW()
WHERE created_at IS NULL;
-- Verify row counts
SELECT
(SELECT COUNT(*) FROM events_source) as source_count,
(SELECT COUNT(*) FROM events_staging) as staging_count,
(SELECT COUNT(*) FROM events) as target_count;
4. Lidar com valores nulos
-- PostgreSQL
INSERT INTO events (id, created_at)
SELECT id, COALESCE(created_at, NOW())
FROM events_source;
-- MySQL
INSERT INTO events (id, created_at)
SELECT id, IFNULL(created_at, NOW())
FROM events_source;
-- SQL Server
INSERT INTO events (id, created_at)
SELECT id, ISNULL(created_at, GETDATE())
FROM events_source;
Exemplos de código por idioma
###JavaScript/Node.js
// Migrate timestamps using Node.js
const mysql = require('mysql2/promise');
const { Pool } = require('pg');
async function migrateTimestamps() {
const mysqlPool = await mysql.createPool({
host: 'mysql-source',
database: 'source_db'
});
const pgPool = new Pool({
host: 'postgres-target',
database: 'target_db'
});
// Get all records
const [rows] = await mysqlPool.query('SELECT * FROM events');
// Transform and insert
for (const row of rows) {
const unixTimestamp = Math.floor(row.created_at.getTime() / 1000);
await pgPool.query(
'INSERT INTO events (id, created_at) VALUES ($1, to_timestamp($2))',
[row.id, unixTimestamp]
);
}
}
###Píton
# Migrate timestamps using Python
import pymysql
import psycopg2
from datetime import datetime
def migrate_timestamps():
# Source connection
mysql_conn = pymysql.connect(
host='mysql-source',
database='source_db'
)
# Target connection
pg_conn = psycopg2.connect(
host='postgres-target',
database='target_db'
)
with mysql_conn.cursor() as cursor:
cursor.execute('SELECT id, created_at FROM events')
rows = cursor.fetchall()
with pg_conn.cursor() as pg_cursor:
for row in rows:
# Convert MySQL datetime to Unix timestamp
unix_ts = int(row[1].timestamp())
pg_cursor.execute(
'INSERT INTO events (id, created_at) VALUES (%s, to_timestamp(%s))',
(row[0], unix_ts)
)
pg_conn.commit()
Lista de verificação de validação
Antes de ir para a produção:
- Compatibilidade de tipo de dados: verifique se a coluna de destino pode conter todos os valores de origem
- Manipulação de fuso horário: confirme se todos os carimbos de data e hora estão em UTC ou documente a política de fuso horário
- Validação de intervalo: verifique se há carimbos de data/hora antes de 1970 ou depois de 2038
- Tratamento NULL: Decida como lidar com carimbos de data/hora NULL/ausentes
- Desempenho: teste a migração em volume de dados representativo
- [] Plano de reversão: tenha um procedimento de reversão verificado
Armadilhas Comuns
1. Ignorando milissegundos
-- Wrong: Losing millisecond precision
INSERT INTO target SELECT created_at FROM source;
-- Correct: Preserve milliseconds
INSERT INTO target
SELECT created_at AT TIME ZONE 'UTC' FROM source;
2. Configuração incorreta de fuso horário
-- Wrong: Assuming local time
INSERT INTO target (created_at)
SELECT created_at FROM source;
-- Correct: Explicit UTC conversion
INSERT INTO target (created_at)
SELECT COALESCE(
created_at AT TIME ZONE 'UTC',
NOW()
) FROM source;
3. Estouro de número inteiro de 32 bits
-- Check for timestamps that will overflow 32-bit
SELECT * FROM events
WHERE created_at > '2038-01-19 03:14:07'::timestamp;
Conclusão
A migração de carimbos de data/hora entre bancos de dados requer planejamento e execução cuidadosos. As principais conclusões são:
- Use carimbos de data/hora Unix para máxima portabilidade
- Sempre valide os dados antes da migração
- Teste minuciosamente com volumes de dados semelhantes aos de produção
- Documente sua estratégia de tratamento de fuso horário
- Tenha um plano de reversão verificado
Seguir essas práticas garantirá que a migração do carimbo de data/hora seja bem-sucedida e possa ser mantida.