Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Quand est-il préférable d'écrire du sql ad hoc par rapport aux procédures stockées

SQL Server met en cache les plans d'exécution pour les requêtes ad hoc, donc (en tenant compte du temps pris par le premier appel) les deux approches seront identiques en termes de vitesse.

En général, l'utilisation de procédures stockées signifie prendre une partie du code nécessaire à votre application (les requêtes T-SQL) et le placer dans un endroit qui n'est pas sous contrôle de source (il peut être, mais généralement n'est pas ) et où il peut être modifié par d'autres à votre insu.

Avoir les requêtes dans un endroit central comme celui-ci peut être une bonne chose, selon le nombre d'applications différentes qui ont besoin d'accéder aux données qu'elles représentent. Je trouve généralement beaucoup plus facile de conserver les requêtes utilisées par une application résidente dans le code de l'application elle-même.

Au milieu des années 1990, la sagesse conventionnelle disait que les procédures stockées dans SQL Server étaient la voie à suivre dans les situations critiques en termes de performances, et à l'époque elles l'étaient définitivement. Cependant, les raisons de cette CW ne sont plus valables depuis longtemps.

Mise à jour : De plus, fréquemment dans les débats sur la viabilité des procédures stockées, la nécessité d'empêcher l'injection SQL est invoquée pour défendre les procs. Sûrement, personne de sensé ne pense que l'assemblage de requêtes ad hoc via la concaténation de chaînes est la bonne chose à faire (bien que cela ne vous expose à une attaque par injection SQL que si vous concaténez l'entrée utilisateur ). De toute évidence, les requêtes ad hoc doivent être paramétrées, non seulement pour éviter le monstre sous le lit d'une attaque par injection SQL, mais aussi pour vous faciliter la vie en tant que programmeur (à moins que vous n'aimiez avoir à déterminer quand utiliser un seul guillemets autour de vos valeurs).

Mise à jour 2 : J'ai fait plus de recherches. Basé sur ce livre blanc MSDN , il semble que la réponse dépende de ce que vous entendez par "ad hoc" avec vos requêtes, exactement. Par exemple, une requête simple comme celle-ci :

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5

... va avoir son plan d'exécution en cache. De plus, étant donné que la requête ne contient pas certains éléments disqualifiants (comme presque tout autre chose qu'un simple SELECT d'une table), SQL Server « paramétrera automatiquement » la requête et remplacera la constante littérale « 5 » par un paramètre, et mettra en cache le plan d'exécution de la version paramétrée. Cela signifie que si vous exécutez ensuite ceci requête ad hoc :

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 23

... il pourra utiliser le plan d'exécution mis en cache.

Malheureusement, la liste des éléments de requête disqualifiants pour le paramétrage automatique est longue (par exemple, oubliez d'utiliser DISTINCT , TOP , UNION , GROUP BY , OR etc.), vous ne pouvez donc vraiment pas compter sur cela pour la performance.

Si vous avez une requête "super complexe" qui ne sera pas paramétrée automatiquement, comme :

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5 OR ITEM_COUNT < 23

... il sera toujours mis en cache par le texte exact de la requête, donc si votre application appelle cette requête avec les mêmes valeurs littérales "codées en dur" à plusieurs reprises, chaque requête après la première réutilisera le plan d'exécution mis en cache (et donc être aussi rapide qu'un proc stocké).

Si les valeurs littérales changent (en fonction des actions de l'utilisateur, par exemple, comme le filtrage ou le tri des données consultées), les requêtes ne bénéficieront pas de la mise en cache (sauf occasionnellement lorsqu'elles correspondent accidentellement exactement à une requête récente).

La façon de bénéficier de la mise en cache avec des requêtes "ad-hoc" est de les paramétrer. Créer une requête à la volée en C# comme ceci :

int itemCount = 5;
string query = "DELETE FROM tblSTUFF WHERE ITEM_COUNT > " + 
        itemCount.ToString();

est incorrect. La manière correcte (en utilisant ADO.Net) serait quelque chose comme ceci :

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();
}

La requête ne contient aucun littéral et est déjà entièrement paramétrée, de sorte que les requêtes suivantes utilisant l'instruction paramétrée identique utiliseront le plan mis en cache (même si elles sont appelées avec des valeurs de paramètre différentes). Notez que le code ici est pratiquement le même que le code que vous utiliseriez pour appeler une procédure stockée de toute façon (la seule différence étant le CommandType et le CommandText), donc cela revient un peu à l'endroit où vous voulez que le texte de cette requête "vive " (dans votre code applicatif ou dans une procédure stockée).

Enfin, si par requêtes "ad hoc", vous entendez que vous construisez dynamiquement des requêtes avec différentes colonnes, tables, paramètres de filtrage et ainsi de suite, comme peut-être ceux-ci :

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

... alors vous ne pouvez pratiquement pas faites cela avec des procédures stockées (sans le EXEC hack dont on ne parle pas dans la société polie), donc le point est sans objet.

Mise à jour 3 : Voici le seul vraiment bon lié aux performances raison (à laquelle je peux penser, de toute façon) pour utiliser une procédure stockée. Si votre requête est longue et que le processus de compilation du plan d'exécution prend beaucoup plus de temps que l'exécution réelle, et que la requête n'est appelée que rarement (comme un rapport mensuel, par exemple), alors la placer dans une procédure stockée peut faites en sorte que SQL Server conserve le plan compilé dans le cache suffisamment longtemps pour qu'il soit encore disponible le mois prochain. Je me demande si c'est vrai ou non, cependant.