Oracle ou PostgreSQL: comparativo técnico sem torcida
O PostgreSQL é o substituto mais próximo do Oracle: PL/pgSQL, tipos ricos, particionamento nativo e extensões. Onde ele empata, onde ainda perde, e o que muda no bolso e na operação.

De todos os bancos livres, o PostgreSQL é o que chega mais perto do Oracle em vocabulário e em ambição. Não é coincidência: os dois vieram da mesma linhagem acadêmica de banco objeto-relacional. Por isso ele é o destino natural de quem quer sair do Oracle sem reescrever a aplicação inteira.
Mas "mais perto" não é "igual". Vamos aos pontos onde a diferença aparece de verdade.
Quadro comparativo
| Critério | Oracle | PostgreSQL |
|---|---|---|
| Linguagem procedural | PL/SQL (packages, tipos, bulk collect) | PL/pgSQL, além de Python, Perl, JavaScript |
| Versionamento MVCC | Undo segments; sem bloat de tabela | Versões na própria tabela; exige VACUUM |
| Particionamento | Opção paga no EE | Nativo e gratuito (declarativo desde o 10) |
| Paralelismo | Muito maduro | Bom, porém menos agressivo |
| Cluster ativo-ativo | RAC | Não existe equivalente nativo |
| Extensibilidade | Fechada | Extensões: PostGIS, pgvector, TimescaleDB… |
| Custo | Licença + 22% ao ano | Zero de licença; suporte opcional |
PL/SQL e PL/pgSQL: 80% igual, 20% sofrido
A estrutura é reconhecível na hora:
-- Oracle (PL/SQL)
CREATE OR REPLACE PROCEDURE aplica_desconto(p_id NUMBER, p_pct NUMBER) AS
v_valor NUMBER;
BEGIN
SELECT valor INTO v_valor FROM pedidos WHERE id = p_id FOR UPDATE;
UPDATE pedidos SET valor = v_valor * (1 - p_pct/100) WHERE id = p_id;
COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN RAISE_APPLICATION_ERROR(-20001, 'Pedido inexistente');
END;
/
-- PostgreSQL (PL/pgSQL)
CREATE OR REPLACE PROCEDURE aplica_desconto(p_id bigint, p_pct numeric)
LANGUAGE plpgsql AS $
DECLARE v_valor numeric;
BEGIN
SELECT valor INTO STRICT v_valor FROM pedidos WHERE id = p_id FOR UPDATE;
UPDATE pedidos SET valor = v_valor * (1 - p_pct/100) WHERE id = p_id;
COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN RAISE EXCEPTION 'Pedido inexistente';
END $;
Os 20% que doem: não existe PACKAGE (usa-se schema como agrupador), transação autônoma exige dblink ou extensão, bulk collect e FORALL têm equivalentes diferentes, hierarquia com CONNECT BY vira WITH RECURSIVE, e sequences se comportam de forma distinta. Nada impossível; tudo trabalhoso quando são dez mil linhas.
Diferenças de comportamento que pegam DBA experiente
- String vazia. No Oracle,
''é NULL. No PostgreSQL, é string vazia. Isso muda o resultado de consultas antigas silenciosamente. - DDL transacional. No PostgreSQL você faz
BEGIN; ALTER TABLE ...; ROLLBACK;e nada aconteceu. No Oracle, DDL dá commit implícito. A favor do Postgres, e delicioso. - MVCC e lixo. O Oracle guarda a versão antiga no undo; o PostgreSQL guarda na própria tabela e precisa de VACUUM. É a maior mudança de rotina operacional — detalhes em VACUUM e bloat.
- Case das identificadores. Oracle guarda em maiúsculas; PostgreSQL em minúsculas. Aspas duplas viram armadilha permanente.
- NUMBER x numeric. Mapear tudo para
numericfunciona, mas custa performance; onde couberbigintouint, use.
O que o PostgreSQL ainda não tem
- RAC. Escrita ativa em vários nós com storage compartilhado não existe nativamente. Patroni resolve failover, não escrita simultânea — veja alta disponibilidade e replicação.
- Flashback Database. Existe PITR, mas não o "desfaz" instantâneo.
- Ferramental unificado. AWR, ADDM e RMAN têm equivalentes espalhados (pg_stat_statements, pgBackRest, auto_explain) e não integrados.
- Suporte de fornecedor único. Existe suporte comercial excelente, mas é um mercado de vários players, não um número de telefone só.
O que o PostgreSQL tem e o Oracle não
Extensões. Um mesmo banco que faz OLTP, guarda geometria com PostGIS, embeddings com pgvector e série temporal com TimescaleDB — tudo em uma transação, um backup, um conjunto de permissões. E tipos nativos que você passa a usar todo dia:
-- Intervalo temporal com restricao de sobreposicao: uma linha faz o trabalho
CREATE EXTENSION IF NOT EXISTS btree_gist;
CREATE TABLE reservas (
sala_id int NOT NULL,
periodo tstzrange NOT NULL,
EXCLUDE USING gist (sala_id WITH =, periodo WITH &&)
);
-- Array e JSONB indexavel, sem tabela auxiliar
CREATE INDEX idx_doc_tags ON documentos USING gin (tags);
A conclusão prática
Para OLTP corporativo, relatórios, aplicações internas e sistemas novos, o PostgreSQL faz o serviço e devolve o orçamento de suporte para o time. Para ERP certificado, RAC obrigatório ou uma base madura de PL/SQL crítico, o Oracle continua defensável — e o caminho é reduzir edição em vez de sair.
Se a decisão for sair, leia antes migrar de Oracle para PostgreSQL: custos e armadilhas. O panorama de custo está em Oracle: custo real, licenciamento e alternativas, e a operação do lado Postgres em guia definitivo de PostgreSQL em produção.
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. Exemplos validados em Oracle 19c e PostgreSQL 16/17.
Fontes: Oracle Database PL/SQL Language Reference 19c; PostgreSQL Documentation — PL/pgSQL, Partitioning, Constraints e Extensions; Oracle Database Licensing Information User Manual.