Eu sei que os procedimentos armazenados são mais eficientes pelo caminho de execução (do que o sql embutido nos aplicativos). No entanto, quando pressionado, não sou super conhecedor do porquê.
Eu gostaria de saber o raciocínio técnico para isso (de uma maneira que eu possa explicar para alguém mais tarde).
Alguém pode me ajudar a formular uma boa resposta?
Respostas:
Acredito que esse sentimento era verdadeiro em um ponto, mas não nas versões atuais do SQL Server. O problema era que, antigamente, as instruções SQL ad hoc não podiam ser otimizadas adequadamente porque o SQL Server só podia otimizar / compilar no nível do lote. Agora temos a otimização no nível de instrução, para que uma consulta adequadamente parametrizada proveniente de um aplicativo possa tirar proveito do mesmo plano de execução que a consulta incorporada em um procedimento armazenado.
Eu ainda prefiro procedimentos armazenados do lado do DBA pelos seguintes motivos (e vários deles podem ter um enorme impacto no desempenho):
sys.sql_modules
, para referências a objetos específicos) facilita muito a vida de todos.SET ANSI_WARNINGS ON
e o outro poderia terSET ANSI_WARNINGS OFF
, e cada um deles teria sua própria cópia do plano. O plano que eles obtêm depende dos parâmetros em uso, estatísticas em vigor etc. na primeira vez em que a consulta é chamada em cada caso, o que pode levar a planos diferentes e, portanto, a um desempenho muito diferente.Dito isso, é provável que essa questão suscite mais argumentos religiosos do que debates técnicos. Se virmos isso acontecendo, provavelmente o desligaremos.
fonte
Embora eu respeite o apresentador, discordo humildemente da resposta fornecida e não por "razões religiosas". Em outras palavras, acredito que não há nenhum recurso fornecido pela Microsoft que diminua a necessidade de orientações para o uso de procedimentos armazenados.
Qualquer orientação fornecida a um desenvolvedor que favorece o uso de consultas SQL de texto bruto deve ser preenchida com muitas ressalvas, de modo que eu acho que o conselho mais prudente é incentivar muito o uso de Procedimentos armazenados e desencorajar suas equipes de desenvolvedores de se envolverem na prática de incorporar instruções SQL no código ou enviar solicitações SQL baseadas em texto simples e brutos, fora dos SPROCs SQL (procedimentos armazenados).
Penso que a resposta simples para a pergunta de por que usar um SPROC é como o apresentador supôs: os SPROCs são analisados, otimizados e compilados. Dessa forma, seus planos de consulta / execução são armazenados em cache porque você salvou uma representação estática de uma consulta e, normalmente, a variará apenas por parâmetros, o que não é verdade no caso de instruções SQL copiadas / coladas que provavelmente se modificam de página para página e componente / camada, e geralmente são variados na medida em que tabelas diferentes, até mesmo nomes de banco de dados, podem ser especificadas de chamada para chamada. Permitindo esse tipo de ad hoc dinâmicoO envio de SQL diminui bastante a probabilidade de o DB Engine reutilizar o plano de consulta para suas instruções ad hoc, de acordo com algumas regras muito estritas. Aqui, estou fazendo a distinção entre consultas dinâmicas ad hoc (no espírito da pergunta levantada) versus o uso do eficiente sistema SPROC sp_executesql.
Mais especificamente, existem os seguintes componentes:
Quando uma instrução SQL é emitida a partir de uma página da web, denominada "instrução ad hoc", o mecanismo procura um plano de execução existente para lidar com a solicitação. Como este é um texto enviado por um usuário, ele será ingerido, analisado, compilado e executado, se for válido. Nesse momento, ele receberá um custo de consulta igual a zero. O custo da consulta é usado quando o mecanismo de banco de dados usa seu algoritmo para determinar quais planos de execução serão removidos do cache.
As consultas ad hoc recebem um valor de custo de consulta original igual a zero, por padrão. Após a execução subsequente do mesmo texto de consulta ad hoc exato, por outro processo do usuário (ou o mesmo), o custo da consulta atual é redefinido para o custo de compilação original. Como nosso custo de compilação de consulta ad hoc é zero, isso não é um bom presságio para a possibilidade de reutilização. Obviamente, zero é o número inteiro menos valorizado, mas por que seria despejado?
Quando surgirem pressões de memória, e ocorrerão se você tiver um site usado com frequência, o mecanismo do DB usará um algoritmo de limpeza para determinar como recuperar a memória que o cache do Procedimento está usando. Ele usa o custo atual da consulta para decidir quais planos despejar. Como você pode imaginar, os planos com custo zero são os primeiros a serem despejados do cache, porque zero significa essencialmente "nenhum usuário atual ou referência a esse plano".
Portanto, é bem provável que esse plano seja despejado primeiro quando surgirem pressões de memória.
Portanto, se você tiver um servidor com muita memória "além de suas necessidades", poderá não ter esse problema com a mesma frequência que um servidor ocupado que tenha apenas memória "suficiente" para lidar com sua carga de trabalho. (Desculpe, a capacidade e a utilização da memória do servidor são um pouco subjetivas / relativas, embora o algoritmo não seja.)
Agora, se estou de fato incorreto sobre um ou mais pontos, certamente estou aberto a ser corrigido.
Por fim, o autor escreveu:
"Agora temos otimização no nível de instrução, para que uma consulta adequadamente parametrizada proveniente de um aplicativo possa tirar proveito do mesmo plano de execução que a consulta incorporada em um procedimento armazenado".
Acredito que o autor esteja se referindo à opção "otimizar para cargas de trabalho ad hoc".
Nesse caso, essa opção permite um processo de duas etapas que evita o envio imediato do plano de consulta completo ao cache do Procedimento. Ele envia apenas um stub de consulta menor lá. Se uma chamada de consulta exata for enviada de volta ao servidor enquanto o stub da consulta ainda estiver no cache do Procedimento, o plano de execução completo da consulta será salvo no cache do Procedimento naquele momento. Isso economiza memória, que durante incidentes de pressão de memória, pode permitir que o algoritmo de remoção despeje seu stub com menos frequência do que um plano de consulta maior que foi armazenado em cache. Novamente, isso depende da memória e utilização do servidor.
No entanto, você precisa ativar esta opção, pois ela está desativada por padrão.
Por fim, quero enfatizar que, muitas vezes, a razão pela qual os desenvolvedores incorporariam o SQL em páginas, componentes e outros locais é porque desejam ser flexíveis e enviar consultas SQL dinâmicas ao mecanismo de banco de dados. Portanto, em um Caso de Uso do mundo real, é improvável que o envio do mesmo texto chamada a chamada seja o mesmo que ocorre com o cache / eficiência que procuramos ao enviar consultas ad hoc ao SQL Server.
Para informações adicionais, consulte:
https://technet.microsoft.com/en-us/library/ms181055(v=sql.105).aspx
http://sqlmag.com/database-performance-tuning/don-t-fear-dynamic-sql
Best,
Henry
fonte
TLDR: Não há diferença significativa de desempenho entre os dois, desde que o seu sql embutido seja parametrizado.
Essa é a razão pela qual eu abandonei lentamente os procedimentos armazenados:
Executamos um ambiente de aplicativos 'beta' - um ambiente paralelo à produção que compartilha o banco de dados de produção. Como o código db está no nível do aplicativo e as alterações na estrutura do db são raras, podemos permitir que as pessoas confirmem novas funcionalidades além do controle de qualidade e façam implantações fora da janela de implantação de produção, mas ainda forneçam funcionalidade de produção e correções não críticas. Isso não seria possível se metade do código do aplicativo estivesse no banco de dados.
Nós praticamos devops no nível do banco de dados (polvo + dacpacs). No entanto, embora a camada de negócios e a subida possam basicamente ser eliminadas, substituídas e a recuperação pelo contrário, isso não é verdade para as mudanças incrementais e potencialmente destrutivas que devem ir aos bancos de dados. Consequentemente, preferimos manter nossas implantações de banco de dados mais leves e menos frequentes.
Para evitar cópias quase exatas do mesmo código para parâmetros opcionais, geralmente usamos um padrão 'where @var é nulo ou @ var = table.field'. Com um processo armazenado, é provável que você obtenha o mesmo plano de execução, apesar de objetivos bastante diferentes, e, portanto, enfrente problemas de desempenho ou elimine planos em cache com dicas de 'recompilação'. Entretanto, com um simples código que acrescenta um comentário de "assinatura" ao final do sql, podemos forçar planos diferentes com base em quais variáveis eram nulas (não devem ser interpretadas como um plano diferente para todas as combinações de variáveis - somente null vs não nulo).
Eu posso fazer mudanças drásticas nos resultados com apenas pequenas alterações rapidamente no sql. Por exemplo, posso ter uma declaração que encerre com dois CTEs, "Raw" e "ReportReady". Não há nada que diga que ambas as CTEs devem ser usadas. Minha instrução sql pode ser:
...
selecione * de {(formato)} "
Isso me permite usar exatamente o mesmo método de lógica de negócios para uma chamada de API simplificada e um relatório que precisa ser mais detalhado, garantindo que eu não duplique a lógica complicada.
Existem razões válidas para usar procs:
Segurança - Você tem outra camada aqui na qual o aplicativo deve passar. Se a conta de serviço do aplicativo não puder tocar nas tabelas, mas apenas tiver permissão de 'executar' nos procs, você terá alguma proteção extra. Isso não o torna um dado, pois tem um custo, mas é uma possibilidade.
Reutilização - Embora eu diria que a reutilização deve ocorrer em grande parte na camada de negócios para garantir que você não esteja ignorando regras de negócios não relacionadas ao banco de dados, ainda temos o tipo local de baixo nível de processos e processos de utilitários "usados em todos os lugares".
Existem alguns argumentos que realmente não suportam procs ou são facilmente mitigados pela IMO:
Reutilização - mencionei isso acima como um "plus", mas também queria mencionar aqui que a reutilização deve ocorrer em grande parte na camada de negócios. Um processo para inserir um registro não deve ser considerado "reutilizável" quando a camada de negócios também pode estar verificando outros serviços não-db.
Inchaço do plano de cache - a única maneira de isso ser um problema é se você estiver concatenando valores em vez de parametrizar. O fato de você raramente obter mais de um plano por processo geralmente o prejudica quando você tem um 'ou' em uma consulta
Tamanho da instrução - um kb extra de instruções sql sobre o nome do processo geralmente será insignificante em relação aos dados que retornam. Se tudo bem para Entidades, tudo bem para mim.
Vendo a consulta exata - Facilitar a localização de consultas no código é tão simples quanto adicionar o local da chamada como um comentário ao código. Tornar o código copiável do código c # para o ssms é tão fácil quanto a interpolação criativa e o uso de comentários:
Injeção de SQL - Parametrize suas consultas. Feito. Na verdade, isso pode ser desfeito se o proc estiver usando sql dinâmico.
Ignorando a implantação - Também praticamos devops no nível do banco de dados, portanto essa não é uma opção para nós.
"Lento no aplicativo, rápido no SSMS" - este é um problema de cache do plano que afeta os dois lados. As opções definidas apenas fazem com que um novo plano seja compilado, o que parece resolver o problema das variáveis THE ONE SET OFF. Isso apenas responde por que você vê resultados diferentes - as opções definidas NÃO corrigem o problema de detectar os parâmetros.
Os planos de execução de sql embutido não são armazenados em cache - simplesmente falso. Uma instrução parametrizada, assim como o nome do processo, é rapidamente dividida em hash e, em seguida, um plano é pesquisado por esse hash. É 100% o mesmo.
Para ser claro, eu estou falando sobre o código SQL inline bruto não gerado a partir de um ORM - usamos apenas o Dapper, que é um micro ORM, na melhor das hipóteses.
https://weblogs.asp.net/fbouma/38178
https://stackoverflow.com/a/15277/852208
fonte