Arquivo de dezembro, 2015

Olá pessoal,

Recentemente passei por uma situação um tanto inusitada no trabalho. Fui acionado para analisar um DELETE com cláusula WHERE em uma subquery que deveria ter dado erro de sintaxe pelo fato do campo utilizado na subquery não fazer parte da tabela da mesma. O SQL Server só não deu erro como quase deletou todos os registros da tabela, e isso só não ocorreu pois o DBA percebeu a demora e cancelou a operação. Ficou confuso? Vamos ao exemplo.

USE tempdb
GO
create table tb01(cod_tb01 int)
create table tb02(cod_tb02 int)
GO
insert into tb01 values(1),(2),(3),(4),(5)
insert into tb02 values(10),(20),(30),(40),(50)
GO
delete from tb01 where cod_tb01 in(select cod_tb01 from tb02)
GO

O DBA que me acionou me falou: O DELETE acima deletou todos os registros da tabela tb01, mas deveria ter dado erro de sintaxe pois a coluna cod_tb01 não faz parte da tabela tb02 e mesmo se fizesse nenhum registro da tb01 existe na tb02. Quando eu executo apenas a subquery o SQL Server dá erro dizendo que a coluna cod_tb01 não faz parte da tabela tb02, mas quando eu executo o statement todo o SQL Server não dá erro de sintaxe e apaga todos os registros da tb01. Por que?

Pois bem, analisando rapidamente e executando apenas a subquery, realmente o campo cod_tb01 não pertence a tabela tb02 e deveria ter dado erro de sintaxe, mas nesse caso acima o SQL Server não reconheceu como erro pois o SELECT da subquery não tem uma relação direta com a query de fora que é o DELETE e o desenvolvedor não usou um alias de tabela para forçar de qual tabela era o campo cod_tb01, ou seja, as queries são independentes para o SQL Server e para o analisador sintático do SQL Server o usuário estava querendo usar propositalmente o campo da tabela tb01 na subquery. Para o SQL Server o DELETE acima teria o mesmo efeito que o DELETE abaixo:

DELETE FROM tb01 WHERE 1 in(SELECT 1 from tb02)

Se o desenvolvedor tivesse escrito o DELETE da forma abaixo teria dado erro de sintaxe, pois da forma abaixo o desenvolvedor está dizendo de qual tabela pertence o campo cod_tb01 e o analisador sintático informaria que não existe o campo cod_tb01 na tabela tb02.

delete from tb01 where cod_tb01 in(select t2.cod_tb01 from tb02 t2)

Analisando o plano de execução do DELETE podemos ver claramente como o SQL Server interpretou a condição WHERE que acabou deletando todos os registros da tabela tb01.

DELETE

Acima podemos ver que o predicado de busca que o SQL Server usou para fazer o WHERE foi:

tb01.cod_01 = tb01.cod_01

Observação:

Esse comportamento não é exclusivo do SQL Server. Juntamente com outro DBA Oracle, executamos o script acima da demonstração e o mesmo comportamento ocorreu no Oracle. Eu acredito que esse comportamento ocorrerá em qualquer banco de dados onde é possível usar subquery, mas não fiz testes em outros SGBDs.

Bom pessoal o que eu quero passar com esse post é:

Tomem cuidado com subqueries. Felizmente nesse caso o DBA cancelou a operação a tempo e os dados não foram apagados. Eu sempre fui contra usar subquery devido a baixa performance que ela tem comparado ao uso de JOIN. A partir de agora sou mais contra ainda o uso de subquery.

Dica: Em muitos casos subqueries são facilmente substituídas pelo uso de JOIN.

Um bom final de ano a todos!

Anúncios