Migrar de Oracle para PostgreSQL: custos e armadilhas reais
O roteiro de uma migração que dá certo: inventário, avaliação de PL/SQL, conversão de tipos, carga com CDC, corte com janela curta — e a lista das armadilhas que estouram cronograma.

Migração de banco não falha por causa de dado. Falha por causa de código, de prazo e de gente. Já participei de algumas que correram bem e de pelo menos duas que viraram história de terror — e a diferença nunca esteve na ferramenta.
Fase 1: inventário honesto (1 a 3 semanas)
Antes de qualquer promessa de prazo, meça o que existe:
-- Volume de codigo no banco: o principal fator de esforco
SELECT type, COUNT(*) AS objetos, SUM(line) AS linhas
FROM dba_source
WHERE owner NOT IN ('SYS','SYSTEM','XDB','DBSNMP','OUTLN')
GROUP BY type ORDER BY 3 DESC;
-- Recursos especificos do Oracle usados no codigo
SELECT name, type, COUNT(*)
FROM dba_source s, (SELECT 'CONNECT BY' t FROM dual UNION ALL
SELECT 'DBMS_SCHEDULER' FROM dual UNION ALL
SELECT 'PRAGMA AUTONOMOUS' FROM dual UNION ALL
SELECT 'BULK COLLECT' FROM dual UNION ALL
SELECT 'UTL_FILE' FROM dual) x
WHERE UPPER(s.text) LIKE '%'||x.t||'%'
GROUP BY name, type;
-- Tamanho e maiores tabelas (define a estrategia de carga)
SELECT segment_name, ROUND(bytes/1024/1024/1024,1) gb
FROM dba_segments WHERE segment_type='TABLE'
ORDER BY bytes DESC FETCH FIRST 20 ROWS ONLY;
Regra de bolso que uso para estimar: até 5 mil linhas de PL/SQL, migração é projeto de meses. Acima de 50 mil, é programa de anos ou candidato a reescrita da aplicação junto.
Fase 2: esquema e tipos
O ora2pg faz o grosso e gera até relatório de esforço estimado (ora2pg -t SHOW_REPORT --estimate_cost). Mas revise o mapeamento de tipos à mão:
| Oracle | PostgreSQL | Cuidado |
|---|---|---|
| NUMBER(p,s) | numeric(p,s) | Se for inteiro, use int/bigint: bem mais rápido |
| NUMBER sem precisão | numeric | Verifique o uso real antes |
| VARCHAR2(n) | varchar(n) ou text | n em bytes x caracteres |
| DATE | timestamp(0) | DATE do Oracle tem hora; o do Postgres não |
| CLOB / BLOB | text / bytea | bytea grande pede Large Object |
| ROWID | ctid (não use) | Se a aplicação usa ROWID, vai doer |
Fase 3: as armadilhas que estouram cronograma
- String vazia é NULL no Oracle. No PostgreSQL não é. Consultas com
WHERE campo IS NULLpassam a devolver resultados diferentes. Esta é, de longe, a fonte número um de bug silencioso. - Ordenação. O Oracle ordena por binário por padrão; o PostgreSQL usa a collation do locale. Relatórios "mudam de ordem" e o usuário abre chamado.
- Divisão por zero e conversões implícitas. O Oracle é permissivo com
'123' + 1; o PostgreSQL não. - Transação autônoma. Muito usada para log de auditoria. No Postgres exige
dblinkou repensar o design. - Hints. Não existem no PostgreSQL (fora
pg_hint_plan). Query afinada na marra volta a depender de estatísticas — leia o query planner imprevisível. - Jobs.
DBMS_SCHEDULERvirapg_cronou agendador externo, com semântica diferente. - VACUUM. Sua rotina operacional muda. Ignorar isso derruba a performance três meses depois do go-live — VACUUM e bloat em escala.
Fase 4: carga e corte
Para bases pequenas, dump e restore numa janela de fim de semana resolve. Acima de algumas centenas de gigabytes, ou com janela curta, o caminho é carga inicial mais CDC:
1. Carga inicial completa (ora2pg COPY ou Debezium snapshot)
2. CDC continuo do Oracle para o Postgres (Debezium/GoldenGate)
3. Rodar em paralelo: aplicacao lendo do Oracle, comparando com o Postgres
4. Corte: congela escrita, espera lag zerar, vira a string de conexao
5. Manter o Oracle de pe por semanas, com rollback ensaiado
Depois da carga, sempre:
ANALYZE; -- sem estatisticas, o planner erra feio
REINDEX DATABASE producao; -- se houve carga fora de ordem
SELECT * FROM pg_stat_user_tables WHERE n_live_tup = 0; -- tabela vazia = carga falhou
Quanto custa
| Item | Observação |
|---|---|
| Consultoria / time de migração | Maior item; proporcional às linhas de PL/SQL |
| Testes e homologação | Costuma ser 40% do projeto; nunca corte aqui |
| Operação em paralelo | Você paga os dois bancos por alguns meses |
| Suporte comercial PostgreSQL | Opcional, uma fração do suporte Oracle |
| Treinamento do time | Barato e com o melhor retorno do projeto |
Compare esse total com dez anos de suporte Oracle — os números de referência estão em custo real e licenciamento. Se a conta não fechar, ainda resta reduzir para Standard Edition 2.
O conselho que eu daria a mim mesmo há vinte anos
Não migre tudo de uma vez. Escolha um sistema periférico, de baixo risco e com dono interessado. Faça ele inteiro, do inventário ao pós-corte. O time aprende, a organização ganha confiança e você descobre as suas armadilhas específicas com um sistema que, se cair, não para a empresa.
Depois disso, o segundo é metade do trabalho. E o terceiro, rotina.
Sobre o autor: Alexandre Almeida atua com bancos de dados há mais de 25 anos, foi instrutor oficial de Oracle e MySQL para mais de 1.000 alunos e escreveu Aprendendo MySQL: Prática, Teoria e Laboratórios. Veja também os treinamentos.
Fontes: documentação do ora2pg; PostgreSQL Documentation — Data Types, Logical Replication e Routine Vacuuming; Oracle Database SQL Language Reference 19c; documentação do Debezium.