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:
O servidor armazena em cache automaticamente os planos de reutilização, conforme necessário; portanto, não há necessidade de chamar esse método diretamente no seu aplicativo cliente.
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 ligar SqlCommand.Prepare()
, mesmo que ele ligue em sp_prepexec
vez de sp_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), o ExecuteNonQuery
ou ExecuteReader
a seguir é totalmente executado e, se for ExecuteReader, ele retorna linhas. Ainda assim, o SQL Server Profiler mostra que a primeira SqlCommand.Execute______()
chamada após a SqlCommand.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ícita sp_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:
- ** O que a preparação realmente poupa é principalmente o tempo necessário para transmitir a sequência de consultas pelo fio.
- Uma consulta preparada não tem garantia de que um plano em cache seja salvo e não é mais rápido que uma consulta ad-hoc quando chega ao servidor.
- Os RPCs (CommandType.StoredProcedure) não ganham nada com a preparação.
- No servidor, um identificador preparado é basicamente um índice em um mapa na memória contendo TSQL para executar.
- O mapa é armazenado por conexão, não é possível o uso de conexão cruzada.
- A redefinição enviada quando o cliente reutiliza a conexão do pool limpa o mapa.
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:
- o único benefício real da chamada
SqlCommand.Prepare()
parece ser que você não precisa enviar o texto da consulta novamente pela rede,
- chamando
sp_prepare
e sp_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 ;-).