Atualizar uma tabela com dados de outra não tem sintaxe única: MySQL usa UPDATE ... JOIN, PostgreSQL usa UPDATE ... FROM, SQL Server usa UPDATE ... FROM com alias e o Firebird não tem junção no UPDATE — resolve com EXISTS ou subconsulta. MERGE é o caminho padrão quando o banco suporta.
O comando muda de banco para banco, mas a armadilha é a mesma em todos: junção que multiplica linha pode atualizar o registro errado sem dar erro. Este guia mostra a sintaxe certa em cada um e como testar antes de rodar.
Como atualizar uma tabela usando dados de outra?
Com uma junção que restrinja as linhas e uma condição que garanta uma linha de origem por linha de destino. Sem essa garantia, o banco escolhe um valor entre os candidatos e segue em frente.
-- intenção: marcar como bloqueado o pedido de cliente inativo
UPDATE PEDIDOS P
SET P.STATUS = 'B'
WHERE P.IDCLIENTE IN (
SELECT C.ID FROM CLIENTES C WHERE C.ATIVO = 0
);
Essa forma com IN funciona em praticamente todos os bancos. As variações por dialeto servem para trazer mais de uma coluna da origem ou para usar junção explícita.
MySQL
UPDATE PEDIDOS P
JOIN CLIENTES C ON C.ID = P.IDCLIENTE
SET P.STATUS = 'B',
P.OBSERVACAO = CONCAT('cliente inativo em ', DATE_FORMAT(NOW(), '%d/%m/%Y'))
WHERE C.ATIVO = 0;
O MySQL aceita JOIN, LEFT JOIN e ORDER BY ... LIMIT no UPDATE (útil para atualizar em lote). Cuidado com LEFT JOIN: linha sem par recebe NULL e sobrescreve o que estava lá.
PostgreSQL
UPDATE PEDIDOS P
SET STATUS = 'B',
OBSERVACAO = 'cliente inativo em ' || to_char(now(), 'DD/MM/YYYY')
FROM CLIENTES C
WHERE C.ID = P.IDCLIENTE
AND C.ATIVO = false;
No PostgreSQL, a tabela do FROM não pode ser a mesma que está sendo atualizada sem alias. E se o FROM casar com mais de uma linha, o resultado é indefinido — o banco não avisa.
SQL Server
UPDATE P
SET P.STATUS = 'B'
FROM PEDIDOS P
JOIN CLIENTES C ON C.ID = P.IDCLIENTE
WHERE C.ATIVO = 0;
Aqui o alias do UPDATE é obrigatório: UPDATE P ... FROM PEDIDOS P. Escrever UPDATE PEDIDOS ... FROM PEDIDOS P gera erro de ambiguidade.
Firebird
O Firebird não aceita JOIN no UPDATE. Duas formas seguras:
-- forma 1: EXISTS, boa para restringir linhas
UPDATE PEDIDOS P
SET P.STATUS = 'B'
WHERE EXISTS (
SELECT 1 FROM CLIENTES C
WHERE C.ID = P.IDCLIENTE
AND C.ATIVO = 0
);
-- forma 2: subconsulta escalar, quando precisa trazer um valor da origem
UPDATE PEDIDOS P
SET P.OBSERVACAO = (
SELECT C.NOME FROM CLIENTES C WHERE C.ID = P.IDCLIENTE
)
WHERE EXISTS (
SELECT 1 FROM CLIENTES C WHERE C.ID = P.IDCLIENTE AND C.ATIVO = 0
);
A subconsulta escalar precisa devolver zero ou uma linha. Se devolver duas, o Firebird levanta erro — o que é melhor do que atualizar com valor arbitrário.
Qual sintaxe usar em cada banco?
| Banco | Forma | Cuidado |
|---|---|---|
| MySQL | UPDATE ... JOIN ... SET |
aceita ORDER BY/LIMIT; LEFT JOIN sobrescreve com NULL |
| PostgreSQL | UPDATE ... SET ... FROM |
tabela-alvo não pode aparecer no FROM sem alias |
| SQL Server | UPDATE alias ... FROM |
alias do UPDATE é obrigatório |
| Firebird | UPDATE + EXISTS ou subconsulta |
não existe JOIN dentro do UPDATE |
| Padrão SQL | MERGE |
Firebird 2.1+, PostgreSQL 15+, MySQL via ON DUPLICATE KEY |
Por que a junção pode atualizar a linha errada?
Porque junção não é filtro: é multiplicação. Se CLIENTES tiver dois registros com o mesmo ID (dado duplicado, chave ausente), o UPDATE encontra duas linhas de origem para o mesmo pedido.
-- detecta origem duplicada antes de rodar o UPDATE
SELECT C.ID, COUNT(*) AS QTD
FROM CLIENTES C
GROUP BY C.ID
HAVING COUNT(*) > 1;
Em banco que aceita junção no UPDATE, como o MySQL, o comando executa e escolhe um dos valores. Em PostgreSQL, o resultado é indefinido. Nenhum dos dois avisa. Por isso a chave de junção precisa ser única — e, se não for, o problema é o dado, não o SQL.
Como testar antes de rodar?
Três passos que custam um minuto e evitam noite em claro:
- Rode o mesmo
WHEREem umSELECTe confira a contagem. - Confirme que a contagem bate com o que você espera atualizar.
- Execute dentro de transação e confira o resultado antes do
COMMIT.
-- teste: quantas linhas serão afetadas?
SELECT COUNT(*)
FROM PEDIDOS P
WHERE P.STATUS <> 'B'
AND P.IDCLIENTE IN (SELECT C.ID FROM CLIENTES C WHERE C.ATIVO = 0);
-- execução controlada (Firebird/isql)
SET TRANSACTION READ WRITE;
UPDATE PEDIDOS P
SET P.STATUS = 'B'
WHERE P.IDCLIENTE IN (SELECT C.ID FROM CLIENTES C WHERE C.ATIVO = 0);
-- conferir
SELECT STATUS, COUNT(*) FROM PEDIDOS GROUP BY STATUS;
ROLLBACK; -- ou COMMIT, depois de conferir
No MySQL, SET SQL_SAFE_UPDATES = 1 bloqueia UPDATE sem chave no WHERE — útil como cinto de segurança em ambiente de desenvolvimento.
E quando o destino é outra tabela, não a mesma linha?
Para sincronizar tabela de resumo ou histórico, MERGE é mais legível que UPDATE com junção e evita o padrão "tenta update, se não existir insere".
MERGE INTO RESUMO_CLIENTE R
USING (
SELECT IDCLIENTE, COUNT(*) AS QTD, SUM(VALOR) AS TOTAL
FROM LANCAMENTOS
GROUP BY IDCLIENTE
) L
ON R.IDCLIENTE = L.IDCLIENTE
WHEN MATCHED THEN
UPDATE SET R.QTD = L.QTD, R.TOTAL = L.TOTAL
WHEN NOT MATCHED THEN
INSERT (IDCLIENTE, QTD, TOTAL) VALUES (L.IDCLIENTE, L.QTD, L.TOTAL);
MERGE existe no Firebird 2.1+, no SQL Server, no Oracle e no PostgreSQL 15+. No MySQL, o equivalente é INSERT ... ON DUPLICATE KEY UPDATE.
Passo a passo para um UPDATE com junção seguro
- Escreva o
SELECTequivalente e confira as linhas. - Verifique se a chave de junção é única.
- Restrinja o
WHEREo máximo possível. - Execute em transação e confira a contagem.
- Faça
COMMITsó depois da conferência. - Guarde o
SELECTde verificação para reutilizar em produção. - Monitore tempo:
UPDATEem massa pode segurar lock em tabela grande.
Checklist
- A chave de junção é única na origem.
- O
SELECTde conferência foi rodado antes. - O
WHERErestringe só o que deve mudar. - O comando usa a sintaxe do banco em uso.
- A execução está em transação com conferência.
- Existe backup ou ponto de restore.
- Tabela grande tem janela de manutenção definida.
Perguntas frequentes
Qual a sintaxe de UPDATE com JOIN no Firebird?
O Firebird não aceita JOIN no UPDATE. Use WHERE EXISTS (SELECT 1 FROM ...) para restringir as linhas ou uma subconsulta escalar no SET para trazer o valor.
UPDATE com JOIN pode atualizar a mesma linha duas vezes?
Não duas vezes, mas pode escolher entre valores diferentes quando a origem tem mais de uma linha para a mesma chave. O resultado é indefinido em PostgreSQL e arbitrário em MySQL.
Como desfazer um UPDATE errado?
Com ROLLBACK, se a transação ainda estiver aberta. Depois do COMMIT, só com backup ou com o valor anterior registrado em tabela de auditoria — e é por isso que a conferência antes do commit não é opcional.
Conclusão
UPDATE com junção é um comando de escrita: a sintaxe muda por banco, mas o risco é comum a todos. Chave única, WHERE restrito, SELECT de conferência e transação cobrem quase todo acidente. O resto é backup.
Quando a query é o gargalo, o Sqlify gera o SQL a partir da descrição em português e explica cada cláusula — inclusive as junções que costumam multiplicar linha.
Leia também: