共有两类基本的system stored procedure:
system stored procedure:T-SQL语言写的,在master数据库中提供的,大部分使用的是sp_前缀,在其他数据库中使用的时候,不需要指定master数据库作为他们的名称的一部分,典型应用于管理任务。
system extended stored procedure:非托管代码写的(主要是C++),通过DLL提供的,大部分使用的是xp_前缀,用来扩展无法使用T-SQL完成的任务。
还可以创建用户自定义的extended stored procedure,然后附加到SQL Server上,现在已经被遗弃,因为extended stored procedure是在SQL Server的内存空间上执行的,这不是执行代码的安全位置,现在推荐使用的是Managed code stored procedures。
以下三类语句不允许在存储过程中使用:
创建其他对象,包括AGGREGATE、RULE、DEFAULT、SCHEMA,FUNCTION、TRIGGER、PROCEDURE、VIEW(这四个还不允许ALTER)
修改和查询计划相关的SET选项,包括PARSEONLY、SHOWPLAN_ALL、SHOWPLAN_TEXT、SHOWPLAN_XML
通过USE语句修改数据库上下文,可以通过使用数据库名称的方式引用另外一个数据库中的对象,但是不允许使用USE databasename语句切换数据库上下文。
创建存储过程需要当前数据库的CREATE PROCEDURE权限和存储过程所属的schema的ALTER权限,使用存储过程时必须保证数据库连接的设置和创建存储过程时是一致的,比如QUOTED_IDENTIFIER、ANSI_NULLS;创建存储过程时如果名称使用了#前缀,表示是一个临时对象,将被创建在tempdb中,服务器重启后将被删除;使用BEGIN...END不是必须的,但这是好的编程实践。
创建存储过程时,使用two-part name引用对象,非常重要,如果仅仅使用了表的名称,没有指定schema名称的话,SQL Server首先查找默认schema,如果没有找到的话,就查找dbo schema,这最小化查询计划被重用的可能,因为直到执行存储过程时,才可以确定引用了哪个schema下的表,如果指定了two-part name的话,避免了引用表的歧义性,最大化SQL Server重用查询计划的可能。
如果创建存储过程时,仅仅提供了存储过程名称的话,SQL Server视图在默认的schema下创建存储过程,存错过程创建位置基于执行的用户的默认schema行为是脆弱的
执行存储过程时,如果只指定了存储过程名称的话,SQL Server会依次在下面位置依次进行查找:
如果存储过程名称以sp_开头的话(用户存储过程不建议这么做),在master数据库的sys schema下查找。
在执行存储过程的用户的默认shema下查找。
当前数据库的dbo schema下查找。
修改存储过程时,不可以修改存储过程类型,比如不能将使用T-SQL创建的存储过程修改为托管代码的存储过程;数据库连接设置将使用修改时的设置,而不是最初创建存储过程时的设置,因此保证修改和创建时的设置一致非常重要;修改时必须提供创建时使用的所有选项(比如WITH ENCRYPTION),否则这些选项都不会被保留。
删除存储过程时需要存储过程所属的schema的ALTER权限或者存储过程本身的CONTROL权限,删除extended stored procedure使用sp_dropextendedproc。
以前版本SQL Server使用sp_depends返回对象之间的依赖关系,这个存储过程有问题不能返回完整信息;现在使用sys.sql_expression_dependencies,每个依赖关系是一行,sys.dm_sql_referenced_entities每个被其他实体引用的实体是一行,sys.dm_sql_referencing_entities每个引用其他实体的实体是一行。
创建存储过程的guidelines:
在存储过程中使用two-part name,尽量指定全名称
保持SET选项的一致性
名称一致性,采用统一的命名规范,不要使用sp_前缀,这些存储过程是用来定义系统存储过程的
使用@@nestlevel查看当前的嵌套层级,最大值是32,在一个存储过程中调用另外一个存储过程就是一次嵌套
保证每个存储过程完成一个任务
使用WITH ENCRYPTION加密存储过程,不过这里的加密的缺点和视图加密是一样的,不是很健壮,有很多第三方工具都可以破解,不建议使用
用户自定义函数不能指定输出参数,每个存储过程都会返回一个整型值,默认是0,用来表示结果值或者状态码,也可以返回业务逻辑相关值,但是应该使用输出参数表示业务逻辑相关值
存储过程的输出参数都有@前缀,数据类型,并且还可以指定默认值,执行存储过程时,既可以按顺序指定参数值,也可以通过名称指定参数值;最好在存储过程最开始处验证输入参数的合法性;输出参数要求类似于输入参数,在定义和使用输出参数时都必须显式指定OUTPUT关键字
为存储过程生成的执行计划大部分情况下是被重用的,这通常都是期望的行为;但是对于不同的参数集合来说有些存储过程可能需要完全不同的执行计划,这类问题称为parameter sniffing,解决办法如下:
在定义存储过程时,添加WITH RECOMPILE选项,这样每次执行时,存储过程都会被重新编译
调用sp_recompile系统存储过程时,对于传递的存储过程都将被标记为非法的,在下次执行时都将被重新编译,也可以向这个存储过程传递表或者视图的名称,这样凡是引用该对象的执行计划都会被重新编译
可以在执行时,指定WITH RECOMPILE选项,这样将重新编译执行计划,但是不会存储生成的执行计划
OPTIMIZE FOR,这是一个查询提示选项,允许你在编译查询时为参数指定值,不管实际执行时参数值是什么
执行上下文就是执行存储过程的安全上下文,用来建立权限标识,由login token和user token表示,一个安全token包括如下内容:
一个服务器或者数据库的principal作为主标识
一或多个principals作为次要标识
0或多个身份认证器
主要和次要标识的特权和权限
Login token:由SQL Server服务实例验证,包括主标识和次要标识,关于服务器级别的许可和任何数据库级别的许可,主标识就是登录本身,次要标识包括从rules和groups继承来的权限
User token:由特定数据库验证,包括关于数据库级别权限的主标识和次要标识,主标识就是数据库用户本身,次要标识就是从数据库roles继承来的许可,User token不能包括服务器角色成员,不鼓励赋予服务器级别许可
EXECUTE AS语句用来设置存储过程的执行上下文,用来重写默认的安全上下文;支持显式模拟,使用单独的EXECUTE AS语句,还支持隐式模拟,在modules上使用EXECUTE AS语句,为了在另外一个用户上执行,必须拥有在那个用户上的IMPERSONATE权限
EXECUTE AS LOGIN或者在服务器范围上的module使用EXECUTE AS语句,其范围是服务器级别的,即切换上下文后,模拟登录的用户拥有的权限的资源都可以访问;EXECUTE AS USER或者在数据库级别module上使用EXECUTE AS语句,意味着引用超出数据库范围外的对象都将返回错误
sys.login_token系统视图展示所有login相关的token,包括login本身以及用户所属角色的token;sys.user_token系统视图展示用户相关的token