Dans le monde SQL Server, il existe deux types de personnes :ceux qui aiment que tous leurs objets soient préfixés et ceux qui ne le font pas. Le premier groupe est divisé en deux catégories :ceux qui préfixent les procédures stockées avec sp_
, et ceux qui choisissent d'autres préfixes (tels que usp_
ou proc_
). Une recommandation de longue date a été d'éviter le sp_
préfixe, à la fois pour des raisons de performances et pour éviter toute ambiguïté ou collision si vous choisissez un nom qui existe déjà dans le catalogue système. Les collisions sont certainement toujours un problème, mais en supposant que vous avez vérifié le nom de votre objet, est-ce toujours un problème de performances ?
Version TL DR :OUI.
Le préfixe sp_ est toujours un non-non. Mais dans cet article, j'expliquerai pourquoi, comment SQL Server 2012 pourrait vous amener à croire que cette mise en garde ne s'applique plus, et quelques autres effets secondaires potentiels du choix de cette convention de dénomination.
Quel est le problème avec sp_ ?
Le sp_
préfixe ne veut pas dire ce que vous pensez qu'il signifie :la plupart des gens pensent sp
signifie "procédure stockée" alors qu'en fait cela signifie "spécial". Procédures stockées (ainsi que tables et vues) stockées dans master avec un sp_
préfixe sont accessibles à partir de n'importe quelle base de données sans référence appropriée (en supposant qu'une version locale n'existe pas). Si la procédure est marquée comme objet système (à l'aide de sp_MS_marksystemobject
(une procédure système non documentée et non prise en charge qui définit is_ms_shipped
à 1), alors la procédure dans master s'exécutera dans le contexte de la base de données appelante. Prenons un exemple simple :
CREATE DATABASE sp_test; GO USE sp_test; GO CREATE TABLE dbo.foo(id INT); GO USE master; GO CREATE PROCEDURE dbo.sp_checktable AS SELECT DB_NAME(), name FROM sys.tables WHERE name = N'foo'; GO USE sp_test; GO EXEC dbo.sp_checktable; -- runs but returns 0 results GO EXEC master..sp_MS_marksystemobject N'dbo.sp_checktable'; GO EXEC dbo.sp_checktable; -- runs and returns results GO
Résultats :
(0 row(s) affected) sp_test foo (1 row(s) affected)
Le problème de performances vient du fait que master peut être vérifié pour une procédure stockée équivalente, selon s'il existe une version locale de la procédure et s'il existe en fait un objet équivalent dans master. Cela peut entraîner une surcharge de métadonnées supplémentaire ainsi qu'un SP:CacheMiss
supplémentaire un événement. La question est de savoir si ces frais généraux sont tangibles.
Considérons donc une procédure très simple dans une base de test :
CREATE DATABASE sp_prefix; GO USE sp_prefix; GO CREATE PROCEDURE dbo.sp_something AS BEGIN SELECT 'sp_prefix', DB_NAME(); END GO
Et procédures équivalentes en master :
USE master; GO CREATE PROCEDURE dbo.sp_something AS BEGIN SELECT 'master', DB_NAME(); END GO EXEC sp_MS_marksystemobject N'sp_something';
CacheMiss :réalité ou fiction ?
Si nous exécutons un test rapide à partir de notre base de données de test, nous constatons que l'exécution de ces procédures stockées n'invoquera jamais réellement les versions de master, que nous qualifions correctement la procédure de base de données ou de schéma (une idée fausse courante) ou si nous marquons le version principale en tant qu'objet système :
USE sp_prefix; GO EXEC sp_prefix.dbo.sp_something; GO EXEC dbo.sp_something; GO EXEC sp_something;
Résultats :
sp_prefix sp_prefix sp_prefix sp_prefix sp_prefix sp_prefix
Exécutons également un Quick TraceSP:CacheMiss
événements :
Nous voyons CacheMiss
événements pour le lot ad hoc qui appelle la procédure stockée (puisque SQL Server ne prend généralement pas la peine de mettre en cache un lot composé principalement d'appels de procédure), mais pas pour la procédure stockée elle-même. Avec et sans le sp_something
procédure existante dans master (et lorsqu'elle existe, avec et sans qu'elle soit marquée comme objet système), les appels à sp_something
dans la base de données utilisateur, n'appelez jamais "accidentellement" la procédure dans master et ne générez jamais de CacheMiss
événements pour la procédure.
C'était sur SQL Server 2012. J'ai répété les mêmes tests ci-dessus sur SQL Server 2008 R2 et j'ai trouvé des résultats légèrement différents :
Ainsi, sur SQL Server 2008 R2, nous voyons un CacheMiss
supplémentaire événement qui ne se produit pas dans SQL Server 2012. Cela se produit dans tous les scénarios (pas de maître d'objet équivalent, un objet dans le maître marqué comme objet système et un objet dans le maître non marqué comme objet système). Immédiatement, j'étais curieux de savoir si cet événement supplémentaire aurait un impact notable sur les performances.
Problème de performances :réalité ou fiction ?
J'ai fait une procédure supplémentaire sans le sp_
préfixe pour comparer les performances brutes, CacheMiss
à part :
USE sp_prefix; GO CREATE PROCEDURE dbo.proc_something AS BEGIN SELECT 'sp_prefix', DB_NAME(); END GO
Donc, la seule différence entre sp_something
et proc_something
. J'ai ensuite créé des procédures wrapper pour les exécuter 1000 fois chacune, en utilisant EXEC sp_prefix.dbo.<procname>
, EXEC dbo.<procname>
et EXEC <procname>
syntaxe, avec des procédures stockées équivalentes vivant dans master et marquées comme objet système, vivant dans master mais pas marquées comme objet système et ne vivant pas du tout dans master.
USE sp_prefix; GO CREATE PROCEDURE dbo.wrap_sp_3part AS BEGIN DECLARE @i INT = 1; WHILE @i <= 1000 BEGIN EXEC sp_prefix.dbo.sp_something; SET @i += 1; END END GO CREATE PROCEDURE dbo.wrap_sp_2part AS BEGIN DECLARE @i INT = 1; WHILE @i <= 1000 BEGIN EXEC dbo.sp_something; SET @i += 1; END END GO CREATE PROCEDURE dbo.wrap_sp_1part AS BEGIN DECLARE @i INT = 1; WHILE @i <= 1000 BEGIN EXEC sp_something; SET @i += 1; END END GO -- repeat for proc_something
En mesurant la durée d'exécution de chaque procédure wrapper avec SQL Sentry Plan Explorer, les résultats montrent que l'utilisation de sp_
préfixe a un impact significatif sur la durée moyenne dans presque tous les cas (et certainement en moyenne) :