Database
 sql >> Base de Dados >  >> RDS >> Database

O prefixo sp_ ainda é um não-não?


No mundo do SQL Server, existem dois tipos de pessoas:aquelas que gostam que todos os seus objetos sejam prefixados e aquelas que não gostam. O primeiro grupo é dividido em duas categorias:aqueles que prefixam procedimentos armazenados com sp_ , e aqueles que escolhem outros prefixos (como usp_ ou proc_ ). Uma recomendação de longa data é evitar o sp_ prefixo, tanto por motivos de desempenho quanto para evitar ambiguidade ou colisões se você escolher um nome que já existe no catálogo do sistema. As colisões certamente ainda são um problema, mas supondo que você tenha verificado o nome do seu objeto, ainda é um problema de desempenho?

TL;versão DR:SIM.


O prefixo sp_ ainda é proibido. Mas neste post vou explicar por que, como o SQL Server 2012 pode levar você a acreditar que esse conselho de advertência não se aplica mais e alguns outros efeitos colaterais potenciais de escolher essa convenção de nomenclatura.

Qual ​​é o problema com sp_?


O sp_ prefixo não significa o que você acha que significa:a maioria das pessoas pensa que sp significa "procedimento armazenado" quando na verdade significa "especial". Procedimentos armazenados (assim como tabelas e visualizações) armazenados no mestre com um sp_ prefix são acessíveis a partir de qualquer banco de dados sem uma referência adequada (assumindo que uma versão local não existe). Se o procedimento estiver marcado como um objeto do sistema (usando sp_MS_marksystemobject (um procedimento de sistema não documentado e não suportado que define is_ms_shipped para 1), então o procedimento em master será executado no contexto do banco de dados chamador. Vejamos um exemplo simples:
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

Resultados:
(0 row(s) affected)

sp_test    foo

(1 row(s) affected)

O problema de desempenho vem do fato de que o master pode ser verificado quanto a um procedimento armazenado equivalente, dependendo se há uma versão local do procedimento e se há de fato um objeto equivalente no master. Isso pode levar a uma sobrecarga extra de metadados, bem como a um SP:CacheMiss adicional evento. A questão é se essa sobrecarga é tangível.

Então vamos considerar um procedimento muito simples em um banco de dados de teste:
CREATE DATABASE sp_prefix;
GO
USE sp_prefix;
GO
CREATE PROCEDURE dbo.sp_something
AS
BEGIN
  SELECT 'sp_prefix', DB_NAME();
END
GO

E procedimentos equivalentes no mestre:
USE master;
GO
CREATE PROCEDURE dbo.sp_something
AS
BEGIN
  SELECT 'master', DB_NAME();
END
GO
EXEC sp_MS_marksystemobject N'sp_something';

CacheMiss:Fato ou Ficção?


Se executarmos um teste rápido em nosso banco de dados de teste, veremos que a execução desses procedimentos armazenados nunca invocará as versões do mestre, independentemente de qualificarmos adequadamente o procedimento pelo banco de dados ou pelo esquema (um equívoco comum) ou se marcarmos o versão master como um objeto do sistema:
USE sp_prefix;
GO
EXEC sp_prefix.dbo.sp_something;
GO
EXEC dbo.sp_something;
GO
EXEC sp_something;

Resultados:
sp_prefix    sp_prefix
sp_prefix    sp_prefix
sp_prefix    sp_prefix

Vamos também executar um Quick Trace® usando o SQL Sentry para observar se há algum SP:CacheMiss eventos:



Vemos CacheMiss eventos para o lote ad hoc que chama o procedimento armazenado (já que o SQL Server geralmente não se incomoda em armazenar em cache um lote que consiste principalmente em chamadas de procedimento), mas não para o procedimento armazenado em si. Com e sem o sp_something procedimento existente no mestre (e quando existe, com e sem ser marcado como um objeto do sistema), as chamadas para sp_something no banco de dados do usuário nunca chame "acidentalmente" o procedimento no mestre e nunca gere nenhum CacheMiss eventos para o procedimento.

Isso foi no SQL Server 2012. Repeti os mesmos testes acima no SQL Server 2008 R2 e encontrei resultados ligeiramente diferentes:



Portanto, no SQL Server 2008 R2, vemos um CacheMiss adicional evento que não ocorre no SQL Server 2012. Isso ocorre em todos os cenários (nenhum objeto mestre equivalente, um objeto no mestre marcado como um objeto do sistema e um objeto no mestre não marcado como um objeto do sistema). Imediatamente fiquei curioso se esse evento adicional teria algum impacto perceptível no desempenho.

Problema de desempenho:fato ou ficção?


Fiz um procedimento adicional sem o sp_ prefixo para comparar o desempenho bruto, CacheMiss a parte, de lado:
USE sp_prefix;
GO
CREATE PROCEDURE dbo.proc_something
AS
BEGIN
  SELECT 'sp_prefix', DB_NAME();
END
GO

Portanto, a única diferença entre sp_something e proc_something . Em seguida, criei procedimentos de wrapper para executá-los 1000 vezes cada, usando EXEC sp_prefix.dbo.<procname> , EXEC dbo.<procname> e EXEC <procname> sintaxe, com procedimentos armazenados equivalentes vivendo no mestre e marcados como um objeto do sistema, vivendo no mestre mas não marcados como um objeto do sistema e não vivendo no mestre.
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

Medindo a duração do tempo de execução de cada procedimento de wrapper com o SQL Sentry Plan Explorer, os resultados mostram que o uso do sp_ prefixo tem um impacto significativo na duração média em quase todos os casos (e certamente na média):