Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Quando é melhor escrever sql ad hoc vs procedimentos armazenados


O SQL Server armazena em cache os planos de execução para consultas ad-hoc, portanto (descontando o tempo gasto pela primeira chamada) as duas abordagens serão idênticas em termos de velocidade.

Em geral, o uso de procedimentos armazenados significa pegar uma parte do código necessário para seu aplicativo (as consultas T-SQL) e colocá-lo em um local que não esteja sob controle de origem (ele pode ser, mas geralmente não é ) e onde pode ser alterado por outros sem o seu conhecimento.

Ter as consultas em um local central como este pode ser uma coisa boa, dependendo de quantos aplicativos diferentes precisam acessar os dados que representam. Geralmente acho muito mais fácil manter as consultas usadas por um aplicativo residente no próprio código do aplicativo.

Em meados da década de 1990, a sabedoria convencional dizia que os procedimentos armazenados no SQL Server eram o caminho a seguir em situações de desempenho crítico, e na época eles definitivamente eram. As razões por trás deste CW não são válidas há muito tempo, no entanto.

Atualização: Além disso, frequentemente em debates sobre a viabilidade de procedimentos armazenados, a necessidade de evitar injeção de SQL é invocada em defesa de procs. Certamente, ninguém em sã consciência pensa que montar consultas ad hoc por meio de concatenação de strings é a coisa correta a fazer (embora isso só o exponha a um ataque de injeção de SQL se você estiver concatenando entrada do usuário ). Obviamente, as consultas ad hoc devem ser parametrizadas, não apenas para evitar o monstro sob a cama de um ataque de injeção de sql, mas também para tornar sua vida como programador geralmente mais fácil (a menos que você goste de ter que descobrir quando usar citações em torno de seus valores).

Atualização 2: Eu fiz mais pesquisas. Com base neste este white paper do MSDN , parece que a resposta depende exatamente do que você quer dizer com "ad-hoc" com suas consultas. Por exemplo, uma consulta simples como esta:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5

... vai ter seu plano de execução armazenado em cache. Além disso, como a consulta não contém certos elementos desqualificadores (como quase qualquer coisa além de um simples SELECT de uma tabela), o SQL Server na verdade "parametrizará automaticamente" a consulta e substituirá a constante literal "5" por um parâmetro e armazenará em cache o plano de execução para a versão parametrizada. Isso significa que se você executar isto consulta ad hoc:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 23

... ele poderá usar o plano de execução em cache.

Infelizmente, a lista de elementos de consulta desqualificados para parametrização automática é longa (por exemplo, esqueça de usar DISTINCT , TOP , UNION , GROUP BY , OR etc.), então você realmente não pode contar com isso para o desempenho.

Se você tiver uma consulta "super complexa" que não será parametrizada automaticamente, como:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5 OR ITEM_COUNT < 23

... ele ainda será armazenado em cache pelo texto exato da consulta, portanto, se seu aplicativo chamar essa consulta com os mesmos valores literais "codificados" repetidamente, cada consulta após a primeira reutilizará o plano de execução em cache (e assim ser tão rápido quanto um proc armazenado).

Se os valores literais forem alterados (com base nas ações do usuário, por exemplo, como filtrar ou classificar dados visualizados), as consultas não se beneficiarão do armazenamento em cache (exceto ocasionalmente quando coincidem exatamente com uma consulta recente).

A maneira de se beneficiar do cache com consultas "ad-hoc" é parametrizá-las. Criando uma consulta em tempo real em C# assim:
int itemCount = 5;
string query = "DELETE FROM tblSTUFF WHERE ITEM_COUNT > " + 
        itemCount.ToString();

está incorreto. A maneira correta (usando ADO.Net) seria algo assim:
using (SqlConnection conn = new SqlConnection(connStr))
{
    SqlCommand com = new SqlCommand(conn);
    com.CommandType = CommandType.Text;
    com.CommandText = 
        "DELETE FROM tblSTUFF WHERE ITEM_COUNT > @ITEM_COUNT";
    int itemCount = 5;
    com.Parameters.AddWithValue("@ITEM_COUNT", itemCount);
    com.Prepare();
    com.ExecuteNonQuery();
}

A consulta não contém literais e já está totalmente parametrizada, portanto, consultas subsequentes usando a instrução parametrizada idêntica usariam o plano em cache (mesmo se chamado com valores de parâmetro diferentes). Observe que o código aqui é praticamente o mesmo que você usaria para chamar um procedimento armazenado de qualquer maneira (a única diferença é o CommandType e o CommandText), então ele se resume a onde você deseja que o texto dessa consulta "viva " (no código do aplicativo ou em um procedimento armazenado).

Finalmente, se por consultas "ad-hoc" você quer dizer que está construindo dinamicamente consultas com diferentes colunas, tabelas, parâmetros de filtragem e outros enfeites, como talvez estes:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5

SELECT ID, FIRSTNAME, LASTNAME FROM tblPEEPS 
    WHERE AGE >= 18 AND LASTNAME LIKE '%What the`

SELECT ID, FIRSTNAME, LASTNAME FROM tblPEEPS 
    WHERE AGE >= 18 AND LASTNAME LIKE '%What the`
    ORDER BY LASTNAME DESC

... então você praticamente não pode faça isso com procedimentos armazenados (sem o EXEC hack que não deve ser falado na sociedade educada), então o ponto é discutível.

Atualização 3: Aqui está o único realmente bom relacionado ao desempenho razão (que eu posso pensar, de qualquer maneira) para usar um procedimento armazenado. Se sua consulta for de longa duração, em que o processo de compilação do plano de execução leva muito mais tempo do que a execução real, e a consulta só é chamada com pouca frequência (como um relatório mensal, por exemplo), colocá-la em um procedimento armazenado pode faça com que o SQL Server mantenha o plano compilado no cache por tempo suficiente para que ele ainda esteja no próximo mês. Me bate se isso é verdade ou não, no entanto.