用sp_开头用户存储过程的名称仍然错误吗?


33

我的一位同事在我们的SQL Server 2008 R2数据库中命名了一个存储过程sp_something。当我看到此消息时,我立即想到:“那是错误的!” 并开始在我的书签中搜索此在线文章该文章解释了错误的原因,因此我可以向我的同事提供解释。

在这篇文章(由Brian Moran撰写)中,解释了给存储过程一个sp_前缀使SQL Server可以在master数据库中查看已编译的计划。由于sp_sproc不在此处,因此SQL Server将重新编译该过程(并为此需要一个专用的编译锁,从而导致性能问题)。

本文提供了以下示例,以显示两个过程之间的区别:

USE tempdb;
GO

CREATE PROCEDURE dbo.Select1 AS SELECT 1;
GO

CREATE PROCEDURE dbo.sp_Select1 AS SELECT 1;
GO

EXEC dbo.sp_Select1;
GO

EXEC dbo.Select1;
GO

您运行此程序,然后打开Profiler(添加“存储过程-> SP:CacheMiss事件”)并再次运行存储过程。您应该看到两个存储过程之间的区别:sp_Select1存储过程将比存储过程生成一个更多的SP:CacheMiss事件Select1(本文引用了SQL Server 7.0SQL Server 2000。

当我在SQL Server 2008 R2环境中运行该示例时SP:CacheMiss,两个过程都得到相同数量的事件(在tempdb和另一个测试数据库中)。

所以我想知道:

  • 我可以在执行示例时做错什么吗?
  • sproc sp_something在新版本的SQL Server中,“不命名用户” adagium是否仍然有效?
  • 如果是这样,是否有一个很好的示例显示其在SQL Server 2008 R2中的有效性?

非常感谢您对此的想法!

编辑

我在msdn上为SQL Server 2008 R2 找到了创建存储过程(数据库引擎),它回答了我的第二个问题:

我们建议您不要使用sp_作为前缀创建任何存储过程。SQL Server使用sp_前缀来指定系统存储过程。您选择的名称可能与将来的某些系统过程冲突。[...]

但是,这里没有提及使用前缀引起的性能问题sp_。我很想知道是否仍然如此,或者他们是否在SQL Server 2000之后解决了问题。


3
我之前确实看过这个,发现性能上的差异可以忽略不计,我将其归结为解决sp_版本的开销稍大(需要检入主数据库和用户数据库,因为它优先考虑系统进程master->用户数据库中的进程->非系统)在特效master
马丁·史密斯

4
您看到给存储过程加上前缀有什么好处sp_?这与为表添加前缀一样有用tbl。为什么要首先让系统搜索主系统(即使它的性能可以忽略不计或没有性能差异)以允许您使用这种无意义的命名约定?
亚伦·伯特兰

1
@AaronBertrand:说实话,我发现 sproc 前面加上sp_ 根本没有好处,只有缺点,而且我自己也永远不会这样。但是,我希望我能说服我的同事也不要这样做。

1
是的,tbl没用,但我仍然喜欢使用它。必须是我的强迫症(OCD)介入。现在下车。
SQLRockstar 2012年

1
也@Josien,你的同事应该有论据是未来制作更复杂的命名方案。让他们解释为什么dbo.sp_Author_Rename比更好dbo.Author_Rename。我想不出一件有意义的事。
亚伦·伯特兰

Answers:


31

这很容易测试自己。让我们创建两个非常简单的过程:

CREATE PROCEDURE dbo.sp_mystuff
AS
  SELECT 'x';
GO
CREATE PROCEDURE dbo.mystuff
AS
  SELECT 'x';
GO

现在,让我们构建一个包装器,执行该包装器多次,无论是否包含模式前缀:

CREATE PROCEDURE dbo.wrapper_sp1
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @i INT = 1;
    WHILE @i <= 1000
    BEGIN
      EXEC sp_mystuff;
      SET @i += 1;
    END
END
GO
CREATE PROCEDURE dbo.wrapper_1
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @i INT = 1;
    WHILE @i <= 1000
    BEGIN
      EXEC mystuff;
      SET @i += 1;
    END
END
GO
CREATE PROCEDURE dbo.wrapper_sp2
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @i INT = 1;
    WHILE @i <= 1000
    BEGIN
      EXEC dbo.sp_mystuff;
      SET @i += 1;
    END
END
GO
CREATE PROCEDURE dbo.wrapper_2
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @i INT = 1;
    WHILE @i <= 1000
    BEGIN
      EXEC dbo.mystuff;
      SET @i += 1;
    END
END
GO

结果:

在此处输入图片说明

结论:

  • 使用sp_前缀较慢
  • 省略模式前缀较慢

更为重要的问题:为什么使用sp_前缀?您的同事希望从中获得什么?这不应该与您不得不证明这种情况更糟有关,而应该与他们证明将相同的三个字母的前缀添加到系统中的每个存储过程有关。我看不到好处。

我还在以下博客文章中对此模式进行了相当广泛的测试:

http://www.sqlperformance.com/2012/10/t-sql-queries/sp_prefix


请注意,这些结果是在SQL Server 2012上得出的。但是您可以在环境中执行相同的测试。
亚伦·伯特兰

1
“您的合作希望从中获得什么”,另请参见匈牙利符号。基本上,这是90年代的事情。另外,在我过去的工作中,标准是给每个存储过程加上前缀,sp_以便可以将它们与其他事物区分开来,并且不会出现名称冲突……我不知道这个性能问题是否存在。
Earlz 2012年

很好的例子,谢谢亚伦。我仍在2008 R2上对其进行测试(并且可能以错误的方式对其进行了测试,导致“ dbo.wrapper_sp1”和“ dbo.wrapper_sp2”似乎比现在的其他两个都快得多)。

12

我们建议您不要使用sp_作为前缀创建任何存储过程。SQL Server使用sp_前缀来指定系统存储过程。您选择的名称可能与将来的某些系统过程冲突。[...]

尽管没有提及任何由于使用sp_前缀引起的性能问题。我很想知道是否仍然如此,或者他们是否在SQL Server 2000之后解决了问题。

正如Martin Smith的简单注释所显示的-是的,如果您有带sp_前缀的存储过程-SQL Server查询执行程序将始终master首先在数据库中检入以查看是否存在该名称的存储过程(标记为系统存储过程)。

而且,如果存在,master数据库中的系统存储过程将始终存在,并且将代替您自己执行该过程。

所以,是的-它依然存在:不使用sp_前缀。


5
测试简单。CREATE PROC dbo.sp_helptext AS SELECT 1然后尝试EXEC dbo.sp_helptext
马丁·史密斯

感谢您的回答,这对mastersp的流行非常有用。

2

一个更好的测试是编写一个需要完全优化的查询,因为这可能更好地反映了您正在编写的proc在做什么。我将以下查询包装在SP中,并重复了测试并获得了相同的结果。

select * from Person.BusinessEntity b
inner join Person.BusinessEntityAddress ba on b.BusinessEntityID = ba.BusinessEntityID
inner join Person.Address a on ba.AddressID = a.AddressID

在这两种情况下,我都得到了相同数量的缓存未命中和命中事件,并且在两种情况下,都将计划添加到了缓存中。我还两次运行了两个proc,并且dm_exec_query_stats报告的CPU时间或经过时间没有一致的差异。

另一个问题是,由于可以从master执行“ sp_” proc,因此您可能会获得运行到master而不是您正在使用的DB的proc的副本,但是快速测试将显示情况并非如此。但是,如果proc从您正在使用的数据库中删除,并且在master中存在一个副本,则它将被执行,如果它是旧版本,则可能会出现问题。如果这是一个问题,我不会使用“ sp_”来命名该过程。


有趣的发现,谢谢!我将您的示例与Aaron的示例结合使用,以运行更多测试。

1

我相信当您不指定完全合格的对象名称时,就必须解决此问题。因此,“ EXEC sp_something”将首先检查主服务器,但“ EXEC dbname.dbo.sp_something”将永远不会先进入主服务器。

我记得,该课程是始终使用完全限定的名称。


5
不要以为那没什么。即使用户数据库中有一个,EXEC MyDB.dbo.sp_helptext 'sp_helptext'仍会使用其中的一个master。AFAIK会检查两个位置,master如果存在并标记为系统对象,则将使用其中的一个。
马丁·史密斯

1
@MartinSmith 2012年我不能强迫主版本将要执行(虽然我的测试中有没有表明有什么事情),除非我放弃了本地副本(在这种情况下MyDB.dbo.sp_foo仍然执行主版)。我现在没有2008/2008 R2来确认此行为在哪里发生了更改。
亚伦·伯特兰

@AaronBertrand-啊,有趣的是我在2008 R2上做了测试。
马丁·史密斯

还要注意的是,如果一个本地过程没有发现一个被师傅发现,后者将被执行,而不会需要被标记为这个系统对象的情况发生。并且至少在2012年,无论主副本是否被标记为系统对象,都不会更改行为-带有或不带有本地db / schema前缀的本地副本始终执行,除非它不存在。
亚伦·伯特兰

1
糟糕,我应该澄清一下,我的评论针对的是建议的答案。SQLRockstar的评论“ EXEC dbname.dbo.sp_something永远不会先成为master。” 是不正确的。
Greenstone Walker
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.