Como fazer MERGE (upsert) numa tabela Delta no Lakehouse
O upsert é a operação que, de uma só vez, atualiza as linhas que já existem e insere as que são novas. Numa tabela Delta do Lakehouse, o comando MERGE faz exatamente isto de forma atómica: evita duplicados e mantém a tabela consistente mesmo quando os dados de origem chegam em lotes. É por isso a base de qualquer carregamento incremental fiável, muito usado para atualizar dimensões e aplicar alterações vindas de um sistema de origem.
Pré-requisitos
- Um workspace com capacidade Microsoft Fabric e um Lakehouse criado.
- Um notebook do Fabric ligado a esse Lakehouse.
- Uma tabela Delta de destino (por exemplo,
dim_clientes) ou vontade de criar uma. - Conhecimentos básicos de SQL ou PySpark.
Passo 1: Preparar a tabela de destino
O upsert precisa de uma tabela Delta de destino. Se ainda não a tens, cria uma pequena tabela de exemplo. No Lakehouse as tabelas são Delta por omissão, mas podes indicá-lo de forma explícita com USING DELTA. O IF NOT EXISTS torna o passo seguro de repetir, sem apagar dados já existentes.
CREATE TABLE IF NOT EXISTS dim_clientes (
cliente_id INT,
nome STRING,
cidade STRING
) USING DELTA;
Passo 2: Criar a origem com os novos dados
A origem é o lote com as linhas novas e as alteradas. Para o exemplo, cria um DataFrame e regista-o como vista temporária, para o poderes referir no SQL. Repara que os clientes 1 e 2 vão servir para atualizar e o 3 é novo, para inserir.
from pyspark.sql import Row
origem = spark.createDataFrame([
Row(cliente_id=1, nome="Ana Silva", cidade="Porto"),
Row(cliente_id=2, nome="Bruno Costa", cidade="Lisboa"),
Row(cliente_id=3, nome="Carla Dias", cidade="Braga"),
])
origem.createOrReplaceTempView("origem_clientes")
Passo 3: Escrever o MERGE em SQL
O MERGE compara o destino com a origem através de uma chave. Quando há correspondência (WHEN MATCHED) atualiza a linha; quando não há (WHEN NOT MATCHED) insere uma nova. Tudo acontece numa única transação, por isso quem estiver a ler a tabela nunca vê um estado a meio. Esta é a forma mais legível de escrever um upsert.
MERGE INTO dim_clientes AS destino
USING origem_clientes AS origem
ON destino.cliente_id = origem.cliente_id
WHEN MATCHED THEN
UPDATE SET destino.nome = origem.nome,
destino.cidade = origem.cidade
WHEN NOT MATCHED THEN
INSERT (cliente_id, nome, cidade)
VALUES (origem.cliente_id, origem.nome, origem.cidade);
Dica: a chave usada no ON deve ser única na origem. Se existirem ids repetidos, o MERGE devolve o erro "multiple source rows matched". Remove os duplicados antes de correr o comando.
Passo 4: A alternativa em PySpark (DeltaTable)
Se preferes Python, a API DeltaTable faz o mesmo upsert sem escreveres SQL. Os métodos whenMatchedUpdateAll e whenNotMatchedInsertAll são atalhos úteis quando as colunas da origem e do destino coincidem; se precisares de controlo fino, usa whenMatchedUpdate e indica apenas as colunas a alterar.
from delta.tables import DeltaTable
destino = DeltaTable.forName(spark, "dim_clientes")
(destino.alias("destino")
.merge(origem.alias("origem"),
"destino.cliente_id = origem.cliente_id")
.whenMatchedUpdateAll()
.whenNotMatchedInsertAll()
.execute())
Verificar o resultado
Consulta a tabela e confirma que o cliente 3 foi inserido e que os clientes 1 e 2 ficaram com os valores mais recentes, sem linhas duplicadas. Se contares as linhas, deves ter exatamente três.
SELECT cliente_id, nome, cidade
FROM dim_clientes
ORDER BY cliente_id;
Para confirmar que o MERGE foi registado como uma nova versão da tabela Delta, corre DESCRIBE HISTORY dim_clientes e procura a operação MERGE na coluna operation.
Conclusão
Com o comando MERGE passas a fazer upserts fiáveis numa tabela Delta do Lakehouse, a base de qualquer carregamento incremental. A seguir, experimenta acrescentar uma condição como WHEN MATCHED AND destino.cidade <> origem.cidade para só atualizar quando algo muda de facto, ou usar uma coluna de data para processar apenas os registos mais recentes. Qual vai ser a primeira tabela do teu Lakehouse a migrar para um upsert com MERGE?