Database
 sql >> Baza danych >  >> RDS >> Database

Czy przedrostek sp_ nadal jest nie-nie?

W świecie SQL Server istnieją dwa typy ludzi:ci, którzy lubią, gdy wszystkie ich obiekty mają prefiksy, i ci, którzy tego nie lubią. Pierwsza grupa jest dalej podzielona na dwie kategorie:ci, którzy poprzedzają procedury składowane przedrostkiem sp_ i tych, którzy wybierają inne prefiksy (takie jak usp_ lub proc_ ). Od dawna zaleca się unikanie sp_ prefiks, zarówno ze względu na wydajność, jak i w celu uniknięcia niejednoznaczności lub kolizji w przypadku wybrania nazwy, która już istnieje w katalogu systemowym. Kolizje z pewnością nadal stanowią problem, ale zakładając, że zweryfikowałeś nazwę swojego obiektu, czy nadal jest to problem z wydajnością?

Wersja TL;DR:TAK.

Prefiks sp_ ​​jest nadal nie-nie. Ale w tym poście wyjaśnię, dlaczego, jak SQL Server 2012 może prowadzić do przekonania, że ​​ta ostrzegawcza rada już nie ma zastosowania, oraz kilka innych potencjalnych skutków ubocznych wyboru tej konwencji nazewnictwa.

Na czym polega problem z sp_?

sp_ prefiks nie oznacza tego, co myślisz, że robi:większość ludzi myśli sp oznacza „procedura składowana”, podczas gdy w rzeczywistości oznacza „specjalny”. Procedury składowane (a także tabele i widoki) przechowywane we wzorcu z sp_ prefiksy są dostępne z dowolnej bazy danych bez odpowiedniego odniesienia (zakładając, że nie istnieje wersja lokalna). Jeśli procedura jest oznaczona jako obiekt systemowy (za pomocą sp_MS_marksystemobject (nieudokumentowana i nieobsługiwana procedura systemowa, która ustawia is_ms_shipped do 1), to procedura w master zostanie wykonana w kontekście wywołującej bazy danych. Spójrzmy na prosty przykład:

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

Wyniki:

(0 row(s) affected)

sp_test    foo

(1 row(s) affected)

Problem z wydajnością wynika z faktu, że master może być sprawdzany pod kątem równoważnej procedury składowanej, w zależności od tego, czy istnieje lokalna wersja procedury i czy faktycznie istnieje równoważny obiekt w master. Może to prowadzić do dodatkowych kosztów metadanych, a także dodatkowego SP:CacheMiss wydarzenie. Pytanie brzmi, czy ten koszt jest namacalny.

Rozważmy więc bardzo prostą procedurę w testowej bazie danych:

CREATE DATABASE sp_prefix;
GO
USE sp_prefix;
GO
CREATE PROCEDURE dbo.sp_something
AS
BEGIN
  SELECT 'sp_prefix', DB_NAME();
END
GO

I równoważne procedury w masterze:

USE master;
GO
CREATE PROCEDURE dbo.sp_something
AS
BEGIN
  SELECT 'master', DB_NAME();
END
GO
EXEC sp_MS_marksystemobject N'sp_something';

CacheMiss:fakt czy fikcja?

Jeśli uruchomimy szybki test z naszej testowej bazy danych, zobaczymy, że wykonanie tych procedur składowanych nigdy nie wywoła w rzeczywistości wersji z wzorca, niezależnie od tego, czy prawidłowo zakwalifikujemy procedurę w oparciu o bazę danych lub schemat (powszechne błędne przekonanie), czy też zaznaczymy wersja główna jako obiekt systemowy:

USE sp_prefix;
GO
EXEC sp_prefix.dbo.sp_something;
GO
EXEC dbo.sp_something;
GO
EXEC sp_something;

Wyniki:

sp_prefix    sp_prefix
sp_prefix    sp_prefix
sp_prefix    sp_prefix

Uruchommy także Quick Trace® używanie SQL Sentry do obserwacji, czy istnieją jakieś SP:CacheMiss wydarzenia:

Widzimy CacheMiss zdarzenia dla partii ad hoc, która wywołuje procedurę składowaną (ponieważ program SQL Server zazwyczaj nie przejmuje się buforowaniem partii, która składa się głównie z wywołań procedur), ale nie dla samej procedury składowanej. Zarówno z, jak i bez sp_something procedura istniejąca w master (i gdy istnieje, zarówno z zaznaczeniem, jak i bez oznaczenia jako obiekt systemowy), wywołania sp_something w bazie danych użytkownika nigdy "przypadkowo" nie wywołuj procedury w master i nigdy nie generuj żadnego CacheMiss wydarzenia dla procedury.

To było na SQL Server 2012. Powtórzyłem te same testy na SQL Server 2008 R2 i znalazłem nieco inne wyniki:

Tak więc w SQL Server 2008 R2 widzimy dodatkowy CacheMiss zdarzenie, które nie występuje w programie SQL Server 2012. Dzieje się tak we wszystkich scenariuszach (brak równoważnego wzorca obiektu, obiekt w wzorcu oznaczony jako obiekt systemowy i obiekt w wzorcu nieoznaczony jako obiekt systemowy). Od razu byłem ciekaw, czy to dodatkowe wydarzenie będzie miało jakiś zauważalny wpływ na wydajność.

Problem z wydajnością:fakt czy fikcja?

Dokonałem dodatkowej procedury bez sp_ prefiks do porównania surowej wydajności, CacheMiss na bok:

USE sp_prefix;
GO
CREATE PROCEDURE dbo.proc_something
AS
BEGIN
  SELECT 'sp_prefix', DB_NAME();
END
GO

Więc jedyna różnica między sp_something i proc_something . Następnie utworzyłem procedury opakowujące, aby wykonać je 1000 razy każda, używając EXEC sp_prefix.dbo.<procname> , EXEC dbo.<procname> i EXEC <procname> składnia, z równoważnymi procedurami składowanymi znajdującymi się w systemie nadrzędnym i oznaczonymi jako obiekt systemowy, znajdującymi się w systemie nadrzędnym, ale nie oznaczonymi jako obiekt systemowy i w ogóle nie działającymi w systemie nadrzędnym.

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

Mierząc czas trwania każdej procedury opakowującej za pomocą SQL Sentry Plan Explorer, wyniki pokazują, że przy użyciu sp_ prefiks ma znaczący wpływ na średni czas trwania w prawie wszystkich przypadkach (i na pewno średnio):