De SQL Server para PostgreSQL: Instância, database e schema. Onde cada coisa mora.

De SQL Server para PostgreSQL: Instância, database e schema. Onde cada coisa mora.

 

SQL Server e PostgreSQL organizam instância, database e schema de formas estruturalmente diferentes — e essa diferença muda completamente como uma query cruza dados entre bancos em cada um.

Francisco Porfírio · Time PostgreSQL da Power Tuning

Na primeira postagem desta série, falamos sobre o primeiro choque de quem sai do wizard do SQL Server no Windows para o apt install/initdb do Linux. Resolvida a instalação, o segundo estranhamento normalmente aparece já no primeiro dia de uso, na primeira vez que alguém tenta fazer um JOIN entre duas bases diferentes no mesmo servidor.

No SQL Server, a resposta usa nomeação de três partes, banco.schema.objeto, dentro da mesma instância. No Postgres, a resposta segue outro caminho, porque a arquitetura é diferente: cada database é uma unidade isolada, com catálogo e visibilidade de transação próprios.

Este post organiza a hierarquia instância → database → schema nos dois bancos, mostra onde cada coisa mora fisicamente em disco, e explica por que uma cross-database query segue caminhos diferentes em cada um — e o que isso muda na prática, na hora de cruzar dados entre databases.

SQL Server (Instância)

1 instância = 1 processo/serviço

BancoAschema: dboClientes BancoBschema: dboPedidos

↔ Referência direta entre bancos

BancoA.dbo.Clientes c

JOIN BancoB.dbo.Pedidos p

Catálogo compartilhado

1 conexãoenxerga BancoA e BancoB

PostgreSQL (Cluster)

1 cluster = 1 processo/serviço

bancoaschema: publicclientes bancobschema: publicpedidos

×

Isto não roda no Postgres:

bancoa.public.clientes c

JOIN bancob.public.pedidos p

Só é possível via postgres_fdw

Conexão 1só vê bancoa Conexão 2só vê bancob

No SQL Server, o catálogo é compartilhado entre databases da instância, permitindo referência direta entre eles.

No Postgres, um JOIN direto entre bancoa e bancob não existe: é preciso importar a tabela via postgres_fdw antes.

Hierarquia instância/cluster → database → schema: SQL Server x PostgreSQL

Como o SQL Server te acostumou a pensar

Uma instância do SQL Server é um único processo (o serviço MSSQLSERVER ou uma instância nomeada) que hospeda vários databases ao mesmo tempo, todos visíveis pela mesma conexão. Dentro de cada database existem um ou mais schemas (o padrão sendo dbo), e dentro de cada schema, os objetos: tabelas, views, procedures.

A nomeação de três partes deixa isso natural no dia a dia:

Isso funciona porque, dentro de uma instância, o SQL Server mantém um catálogo de metadados e um gerenciador de locks/transações compartilhado entre todos os databases. O USE apenas troca o database “padrão” de contexto para nomes sem prefixo — mas qualquer objeto de qualquer outro database da mesma instância continua acessível via nome completo, no mesmo plano de execução, na mesma transação.

A filosofia do Postgres: cada database é um cofre isolado

Lembra do “cluster” que criamos no post anterior com o initdb? Esse é o equivalente mais próximo de uma instância: um único processo postgres, ouvindo em uma porta, capaz de hospedar múltiplos databases. Até aqui, parecido com o SQL Server.

A diferença aparece na camada de baixo: no Postgres, cada database tem seu próprio catálogo de sistema (as tabelas internas que descrevem tabelas, colunas, índices, permissões etc.) e sua própria visibilidade de transações (MVCC). Fisicamente, cada database vive em um diretório separado dentro do cluster:

E, consequência direta disso: uma conexão no Postgres se autentica em um único database por vez. Não existe um USE outrobanco; que troque o contexto dentro da mesma sessão — trocar de database significa abrir uma nova conexão. No psql, o comando mais próximo disso é \c — mas por baixo dos panos ele fecha a conexão atual e abre outra.

E se eu tentar mesmo assim? O Postgres nem deixa passar pelo parser. Tentar referenciar um database no nome do objeto retorna um erro bem direto ao ponto: ERROR: cross-database references are not implemented. Não é uma permissão faltando, é a arquitetura dizendo que aquele caminho não existe.

Onde entra o schema

Dentro de cada database, o Postgres também organiza objetos em schemas — o conceito em si é idêntico ao do SQL Server. A diferença é só o nome do padrão: em vez de dbo, todo database novo já nasce com um schema chamado public.

A nomeação de objetos no Postgres é, portanto, de duas partes, não três: schema.objeto. O database nunca entra no nome, porque ele já está implícito na conexão atual:

Repare que cruzar schemas diferentes do mesmo database funciona sem nenhuma restrição especial — o limite é sempre o database, nunca o schema.

Passo a passo: criando database e schema no Postgres

O search_path é o equivalente funcional do schema padrão do usuário no SQL Server: define em qual schema o Postgres procura um objeto quando você não escreve o prefixo. Sem configurar nada, o padrão de fábrica já inclui public.

De onde vem um database novo? Assim como o SQL Server usa o database model como molde para todo database criado, o Postgres clona o database template1 por padrão em todo CREATE DATABASE/createdb. Alterar algo em template1 (uma extensão, uma função utilitária) propaga para todo database novo criado depois. Já template0 é um molde “de fábrica”, intocado, usado para restaurar dumps com encoding/collation diferentes do padrão do cluster.

Por que cross-database query segue caminhos diferentes em cada banco

É uma decisão de arquitetura presente desde as origens do Postgres: cada database é uma unidade de isolamento completa, com catálogo próprio, controle de acesso próprio e visibilidade de transação própria. Não existe join de execução única entre databases dentro do motor — qualquer cruzamento passa por uma camada explícita.

Quando você realmente precisa cruzar dados entre databases no mesmo cluster (ou até em servidores diferentes), as saídas comuns são:

  • postgres_fdw — a opção mais robusta e recomendada hoje em dia. Cria “tabelas estrangeiras” que apontam para tabelas em outro database (mesmo servidor ou remoto), permitindo usá-las quase como locais, inclusive com algum push-down de filtros para performance. É o equivalente conceitual do Linked Server, explicado no quadro abaixo.
  • dblink — extensão mais antiga, baseada em funções que executam queries em outro database e retornam o resultado. Mais simples de usar pontualmente, porém sem otimizações que o postgres_fdw oferece.
  • Join em nível de aplicação — abrir duas conexões, buscar os dados de cada lado, e cruzar em memória na aplicação. Mais trabalho, mas evita acoplar bancos logicamente separados.
  • Repensar a separação em databases — se dois “bancos” precisam de JOINs frequentes entre si, muitas vezes o desenho correto no Postgres é um único database com múltiplos schemas, já que cruzar schemas do mesmo database não tem nenhuma restrição.

Pensando como DBA SQL Server: a analogia mais próxima de um postgres_fdw é o Linked Server. Os dois resolvem o mesmo problema — enxergar objetos de outro banco como se fossem locais — e os dois têm o mesmo tipo de pegadinha: parecem transparentes na sintaxe, mas por baixo geram uma conexão separada, com custo de rede/serialização e comportamento transacional próprio (nada de BEGIN TRAN cobrindo os dois lados de graça). A diferença prática está no grau de exposição: um Linked Server, uma vez criado, expõe o catálogo inteiro do servidor remoto automaticamente; o postgres_fdw é mais granular — você importa explicitamente, tabela por tabela ou schema por schema, via IMPORT FOREIGN SCHEMA/CREATE FOREIGN TABLE, o que tende a deixar a superfície de acesso mais controlada.

Quer custo zero? Use schemas, não databases. No Postgres, um JOIN entre schemas do mesmo database é nativo, sem nenhuma camada extra — o planejador enxerga as duas tabelas normalmente e otimiza como qualquer outro JOIN. Já um JOIN entre databases diferentes sempre passa por postgres_fdw/dblink, mesmo que os dois databases estejam no mesmo cluster, na mesma máquina física — e essa camada tem custo real: serialização dos dados, tráfego de rede/loopback e, em geral, menos otimizações de plano do que um JOIN nativo. Se o objetivo é evitar esse custo, a resposta arquitetural correta é databases diferentes viram schemas diferentes dentro de um único database.


Comparativo lado a lado

Conceito SQL Server PostgreSQL
Nível mais alto Instância Cluster (criado pelo initdb)
Schema padrão dbo public
Database molde para novos databases model template1
Nomeação completa de objeto 3 partes: banco.schema.objeto 2 partes: schema.objeto
Trocar de database na mesma sessão USE outrobanco; Não existe — requer nova conexão (\c no psql)
JOIN entre databases da mesma instância Referência direta via catálogo compartilhado Via camada explícita: postgres_fdw/dblink
JOIN entre schemas do mesmo database Referência direta Referência direta
Catálogo de metadados Compartilhado entre databases da instância Isolado por database

Erros comuns de quem vem do SQL Server

  • Escrever nome de três partes por hábito. bancoa.public.clientes só funciona se bancoa for o próprio database da conexão atual — o Postgres aceita o prefixo, mas ele precisa “bater” com o database conectado, não pode apontar para outro.
  • Achar que \c é igual a um USE. É uma reconexão de verdade: fecha a sessão TCP atual e abre outra, o que também derruba qualquer estado de sessão (variáveis temporárias, tabelas temp, transação em aberto).
  • Modelar um database por cliente/tenant achando que vai poder cruzar dados facilmente depois. Se o plano é multi-tenant com necessidade de relatórios cruzando tenants, schemas separados dentro de um único database costumam ser um desenho mais simples de manter no Postgres do que databases separados.
  • Esperar que uma transação cubra os dois lados de um dblink/postgres_fdw. São conexões distintas por baixo dos panos; garantias de atomicidade entre os dois databases exigem cuidado extra (two-phase commit manual, ou aceitar consistência eventual), além de compreender a adição do custo de rede a operação.

Resumo

No SQL Server, a instância compartilha um único catálogo entre todos os databases que hospeda, permitindo referência direta entre eles. No Postgres, o cluster hospeda vários databases, mas cada um mantém catálogo e visibilidade de transação próprios, e cada conexão fica associada a um único database. Schemas continuam sendo o nível de organização dentro do database, sem nenhuma restrição extra entre si. Quando o cruzamento entre databases é realmente necessário, postgres_fdw é a ponte mais robusta disponível hoje — e muitas vezes a pergunta certa é se aqueles “dois bancos” não deveriam, desde o início, ser dois schemas de um único database.

Tem PostgreSQL em produção na sua empresa?

Faça o Power PostgreSQL Check-up: uma avaliação gratuita de performance, backup, segurança e alta disponibilidade, feita pelos nossos especialistas. Sem custo e sem compromisso.

Quero meu check-up gratuito

 

Tags: , , , , , , ,

Power Tuning
Visão geral da privacidade

Este site utiliza cookies para que possamos lhe proporcionar a melhor experiência de usuário possível. As informações dos cookies são armazenadas em seu navegador e executam funções como reconhecê-lo quando você retorna ao nosso site e ajudar nossa equipe a entender quais seções do site você considera mais interessantes e úteis.