Me deparei com o código do desenvolvedor em que o método SqlCommand.Prepare () (consulte MSDN) é amplamente utilizado antes da execução de consultas SQL. E eu me pergunto qual é o benefício disso?
Amostra:
command.Prepare();
command.ExecuteNonQuery();
//...
command.Parameters[0].Value = 20;
command.ExecuteNonQuery();
Eu brinquei um pouco e segui. A execução do comando após chamar o Prepare()
método faz com que o Sql Server execute a seguinte instrução:
declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,N'@id int,@desc text',N'INSERT INTO dbo.testtable (id) VALUES (@id)',@id=20'
select @p1
Depois disso, quando o parâmetro obtém seu valor e SqlCommand.ExecuteNonQuery()
é chamado, o seguinte é executado no Sql-Server:
exec sp_execute 1,@id=20
Para mim, parece que a instrução é compilada assim que Prepare()
é executada. Gostaria de saber qual é o benefício disso? Isso significa que ele é colocado no cache do plano e pode ser reutilizado assim que a consulta final for executada com os valores de parâmetro desejados?
Eu descobri (e o documentei em outra pergunta ) que SqlCommands que são executados com SqlParameters sempre são agrupados em sp_executesql
chamadas de procedimento. Isso torna o Sql Server capaz de armazenar e reutilizar os planos independentemente dos valores dos parâmetros.
Com relação a isso, pergunto-me se o prepare()
método é meio inútil ou obsoleto ou se estou perdendo alguma coisa aqui.
Respostas:
Preparar um lote SQL separadamente da execução do lote SQL preparado é uma construção efetivamente **inútil para o SQL Server, considerando como os planos de execução são armazenados em cache. Separar as etapas de preparação (análise, vinculação de quaisquer parâmetros e compilação) e execução somente faz sentido quando não houver armazenamento em cache. O objetivo é economizar o tempo gasto na análise e compilação reutilizando um plano existente, e é isso que o cache do plano interno faz. Mas nem todos os RDBMSs fazem esse nível de armazenamento em cache (claramente a partir da existência dessas funções) e, portanto, fica a cargo do código do cliente solicitar que um plano seja "armazenado em cache", salve o ID do cache e reutilize-o isto. Isso representa um ônus adicional para o código do cliente (e para o programador se lembrar de fazê-lo e corrigi-lo) que é desnecessário. De fato, a página do MSDN para o método IDbCommand.Prepare () declara:
Deve-se observar que, na medida em que meus testes mostram (que corresponde ao que você mostra na pergunta), chamar SqlCommand.Prepare () , não executa uma operação "preparar" apenas: chama sp_prepexec que prepara e executa o SQL ; ele não chama sp_prepare, que é apenas analisado e compilado (e não recebe nenhum valor de parâmetro, apenas seus nomes e tipos de dados). Portanto, não pode haver nenhum benefício de "pré-compilação" da chamada,
SqlCommand.Prepare
pois ela executa uma execução imediata (supondo que o objetivo seja adiar a execução). No entanto , há um benefício potencial interessante menor de ligarSqlCommand.Prepare()
, mesmo que ele ligue emsp_prepexec
vez desp_prepare
. Por favor, veja a nota (** ) na parte inferior para obter detalhes.Meus testes mostram que, mesmo quando
SqlCommand.Prepare()
é chamado (e observe que esta chamada não emite nenhum comando no SQL Server), oExecuteNonQuery
ouExecuteReader
a seguir é totalmente executado e, se for ExecuteReader, ele retorna linhas. Ainda assim, o SQL Server Profiler mostra que a primeiraSqlCommand.Execute______()
chamada após aSqlCommand.Prepare()
chamada é registrada como um evento "Prepare SQL" e as.Execute___()
chamadas subseqüentes são registradas como eventos "Exec Prepared SQL".Código de teste
Foi carregado no PasteBin em: http://pastebin.com/Yc2Tfvup . O código cria um aplicativo de console .NET / C # destinado a ser executado enquanto um rastreamento do SQL Server Profiler estiver em execução (ou uma sessão de Eventos Estendidos). Ele pausa após cada etapa, para que fique claro quais instruções têm um efeito específico.
ATUALIZAR
Encontrei mais informações e um pequeno motivo potencial para evitar chamadas
SqlCommand.Prepare()
. Uma coisa que notei nos meus testes e o que deve ser observado para qualquer pessoa que execute esse teste do aplicativo Console: nunca há uma chamada explícitasp_unprepare
. Eu pesquisei e encontrei a seguinte postagem nos fóruns do MSDN:SqlCommand - Preparar ou não preparar?
A resposta aceita contém várias informações, mas os pontos mais importantes são:
Também encontrei a seguinte postagem da equipe do SQLCAT, que parece estar relacionada, mas poderia ser simplesmente um problema específico do ODBC, enquanto o SqlClient é limpo corretamente ao descartar o SqlConnection. É difícil dizer, já que a postagem do SQLCAT não menciona nenhum teste adicional que ajudaria a provar isso como uma causa, como limpar o pool de conexões etc.
Cuidado com as instruções SQL preparadas
CONCLUSÃO
Dado que:
SqlCommand.Prepare()
parece ser que você não precisa enviar o texto da consulta novamente pela rede,sp_prepare
esp_prepexec
armazenando o texto da consulta como parte da memória da conexão (ou seja, memória do SQL Server)Eu recomendaria não ligar,
SqlCommand.Prepare()
porque o único benefício potencial é economizar pacotes de rede enquanto a desvantagem está ocupando mais memória do servidor. Embora a quantidade de memória consumida seja provavelmente muito pequena na maioria dos casos, a largura de banda da rede raramente é um problema, já que a maioria dos servidores de banco de dados está diretamente conectada aos servidores de aplicativos com mais de 10 Megabit de conexões no mínimo (provavelmente 100 Megabit ou Gigabit atualmente) ( e alguns estão na mesma caixa ;-). Isso e a memória são quase sempre um recurso mais escasso do que a largura de banda da rede.Suponho que, para quem escreve software que pode se conectar a vários bancos de dados, a conveniência de uma interface padrão pode alterar essa análise de custo / benefício. Mas, mesmo nesse caso, tenho que acreditar que ainda é fácil abstrair uma interface de banco de dados "padrão" que permita diferentes provedores (e, portanto, diferenças entre eles, como não precisar chamar
Prepare()
), considerando que isso é um objeto básico Programação orientada que você provavelmente já está fazendo no código do seu aplicativo ;-).fonte
sp_prepare
chamada parasp_executesql
, apenas para ter certeza. Para a etapa 10, sim, fiz mais testes ontem e vi algumas ocasiões com alças diferentes parasp_prepare
, mas ainda assim nuncasp_executesql
. Vou testar mais uma coisa e atualizar minha resposta.sp_executesql
não pode ou não. Mas, independentemente disso, o tempo de análise ainda estará lásp_prepare
se o plano tiver sido removido do cache, então removerei essa parte da minha resposta (interessante, mas irrelevante). De qualquer forma, essa parte foi mais uma observação interessante, pois não abordou sua pergunta direta. Tudo o resto ainda se aplica.Eu diria que o método Prepare tem um valor limitado no mundo SqlCommand, não que seja totalmente inútil. Um benefício é que o Prepare faz parte da interface IDbCommand que o SqlCommand implementa. Isso permite que o mesmo código seja executado em outros provedores de DBMS (que podem exigir Preparação) sem lógica condicional para determinar se a Preparação deve ser chamada ou não.
A preparação não tem valor com o .NET Provider for SQL Server além desses casos de uso, o AFAIK.
fonte