Recentemente fui procurado por um colega desenvolvedor para ajudá-lo a resolver um problema que, segundo ele, estava lhe tirando o sono.
O problema parecia muito simples à primeira vista: era necessário escrever uma declaração SQL para atualizar um campo Matched caso um ID de usuário aparecesse em duas tabelas que eram alimentadas com dados de sistemas diferentes.
A coisa parecia elementar, até que meu colega observou um detalhe: as tais tabelas possuíam chaves primárias compostas. Isto é, a chave primária era definida por uma combinação de colunas ao invés de um única coluna, como acontece no caso das chaves primárias simples. E este detalhe faz muita diferença na implementação da solução.
Alguns SGBDs, como o SQL SERVER, suportam o uso de junções de tabelas dentro de declarações UPDATE, o que facilita em alguns casos especiais. Mas a maioria dos SGBDs que eu conheço não aceita esta sintaxe.
Vejamos alguns exemplos de sintaxe.
Digamos que o problema descrito acima envolvesse apenas chaves simples (a coluna UserID). Se eu usasse o SQL SERVER, poderia escrever assim:
--sintaxe SQL SERVER UPDATE MyTable SET Matched = 1 FROM MytTable M INNER JOIN OtherTable O ON M.UserID = O.UserID
Uma maneira mais usual de se escrever isso e que seria aceita DB2, ORACLE, POSTGRES e vários outros, seria usando o operador IN na cláusula WHERE.
-- sintaxe genérica (DB2, ORACLE, SQL SERVER, POSTGRES...) UPDATE MyTable SET Matched = 1 WHERE UserID IN (SELECT UserID FROM OtherTable)
O problema é que esta solução só funciona quando temos uma única coluna na cláusula WHERE. Ou seja, isso não resolveria o problema de uma tabela com chave primária composta. E como nosso SGBD não suportava uso de JOIN na declaração de UPDATE, tive que encontrar outra estratégia.
Na solução que eu propus para o problema inclui a cláusula WHERE, mas usa outro operador: EXISTS. Este operador é pouco usado e muitas vezes esquecido. Mas cai como uma luva na solução deste problema.
O EXISTS testa se uma subconsulta retorna algum registro. Se retornar 1 ou mais registros, o resultado é VERDADEIRO. Se for vazia, o resultado é FALSO.
No caso em questão, as tabelas envolvidas (MyTable e OtherTable) possuem chaves primárias compostas por dois campos : UserID e Account. A ideia é combinar as chaves das duas tabelas dentro da subconsulta.
Com estas informações, não foi difícil montar uma nova declaração UPDATE, como mostro a seguir:
UPDATE MyTable M SET M.Matched = 1 WHERE EXISTS ( SELECT 1 FROM OtherTable O WHERE O.UserID= M.UserID AND O.Account = M.Account )
Um detalhe: com a consulta acima faz um teste baseado na chave primária das tabelas, é de se esperar que o UPDATE rode com uma boa performance.
Esta solução acabou sendo implementada e a aplicação que a usa vai muito bem, obrigado.
Por hoje é só. Abraço!