- 许多SQL Server的组件都可以扩展,过去扩展数据库引擎都是通过extended stored procedures,其问题是编程复杂,基于C++的,直接在SQL Server的处理空间上执行,这个位置不是一个安全执行位置,任何错误都可能引起数据库引擎不稳定;即使最不安全的托管代码也比非托管代码安全
- 托管代码的优点:
- 类型安全性
- 丰富的类库
- 高级的异常处理
- 有些情形性能更高
- 在托管代码中可以创建的数据库对象:用户自定义函数、存储过程、触发器(DML、DDL),托管代码还允许创建其他类型对象:自定义数据类型、自定义的聚合函数
- T-SQL是在数据库中操作数据的主要方法,它被设计用来直接的数据访问,提供高性能,尤其是在大数据量的情况下,但是T-SQL不是完整的高级语言,没有面向对象的能力,不是为密集运算、字符串处理等任务设计的,所有对象只能在单一扁平的命名空间中,即不允许在命名空间中在包含命名空间,提供了一组有用的内建函数
- 托管代码提供了完整的面向对象能力,适合于密集型运算(比如加密)和字符串处理
- 需求越是面向数据,越是应该使用T-SQL;越是面向计算、字符串处理、外部资源访问,越是应该使用托管代码
- 适合在托管代码中使用的数据库对象,总的来说都是执行和数据无关或者关连性不高的任务时使用托管代码,如果执行和数据密切相关的任务时还是应该使用T-SQL代码:
数据库对象 |
T-SQL |
托管代码 |
标量函数 |
性能差 |
如果没有数据访问的话是好的选择 |
表值函数 |
如果和数据相关的话是好的选择 |
不是非常和数据相关的话是好的选择 |
存储过程 |
好的选择 |
数据无关的外部访问是好的选择 |
DML触发器 |
好的选择 |
通常和数据高度相关,很少是好的选择 |
DDL触发器 |
如果是EVENTDATA的有限的XML处理的话还可以 |
扩展EVENTDATA的XML处理的好的选择 |
聚合 |
无法实现 |
好的选择 |
用户自定义类型 |
仅仅支持alias types |
好的选择 |
- 允许管理员配置的控制服务器安全性和完整性的安全许可共有三个:SAFE,只允许访问SQL Server数据库;EXTERNAL_ACCESS允许访问本地或者网络资源、注册表;UNSAFE,允许调用非托管代码,使用该选项时必须必须非常谨慎
- EXTERNAL_ACCESS和UNSAFE安全许可需要额外的配置,一共有两种方式:
- 使用ALTER DATABASE SET TRUSTWORTHY ON将数据库标记为TRUSTWORTHY ,通常来说,如果没有完全理解该操作都修改了数据库的哪些安全环境的话,不推荐使用该方法
- 在master数据库中创建assembly file时,创建一个asymmetric key,登录时映射到这个key,这是使用EXTERNAL_ACCESS和UNSAFE许可权限的推荐方法
- 使用CREATE ASSEMBLY语句将程序集catalog在当前数据库中,SQL Server基于WITH PERMISSON_SET语句为程序集赋予权限,默认是SAFE权限;在执行用户自己创建的程序集前,必须在数据库级别将clr enabled选项设置1(enabled),该选项只是控制程序集和其中对象的运行,该选项不开启,依然可以创建程序集极其对象;程序集一旦catalog,程序集就被包含在数据库中,原来的程序集文件就不在被SQL Server需要,以后执行时都是从数据库中加载程序集,而不是文件系统中
- 程序集可以在以下位置上创建:
- 本地文件
- UNC路径(比如\\SERVER\Share\PathToFile)
- dll文件的二进制字符串,如果用于直接从Visual Studio部署,因为你可能没有访问服务器文件系统的权限
- 使用CREATE FUNCTION...EXTERNAL NAME语句创建使用托管代码创建的用户自定义函数,SQL Server中的函数名称和程序集中定义的名称可以不一致,但是建议采用相同的命名;函数中参数名称在T-SQL中和托管代码中可以不一致,但是尽量保持一致,如果因为在SQL Server中编译无法通过的话,尽量修改托管代码中参数的名称;自动部署的特性是SqlFunctionAttribute
- EXTERNAL NAME用于指定方法在程序集中的位置,不论数据库的配置还是编程语言是否区分大小写,路径都是区分大小写的,通常由三部分名称构成:
- 第一部分表示程序集别名,在CREATE ASSEMBLY时指定
- 第二部分名称表示包含方法的全类名
- 第三部分指定方法名称
- 在托管代码中创建存储过程时,SqlPipe用于返回数据行,如果只为该对象的Send方法传递字符串的话,相当于T-SQL的PRINT语句;和存储过程相关的特性是SqlProcedure;使用托管代码创建存储过程通常用来访问外部资源,需要EXTERNAL_ACCESS权限,使用该方法避免了使用扩展存储过程,扩展存储过程需要在文件系统级别执行操作,通常不太安全,使用托管代码是更好的选择
- 可以使用托管代码创建DML和DDL触发器,DML触发器在托管代码中也可以访问inserted和deleted虚拟表(和T-SQL代码中的表名称是一样的),DDL触发器也可以访问XML EVENTDATA结构;一个DML触发器可以关联多个事件,使用SqlTriggerContext可以获取触发器上下文,获取触发的事件;和触发器关联的特性是SqlTriggerAttribute,Name属性指定部署到SQL Server中触发器名称,Target指定触发器附加到对象的名称,Event指定触发触发器的事件名称
- 用户自定义聚合函数是一个完全新的对象类型,不能使用T-SQL代码创建,允许你提供SQL Server内建聚合函数不支持的聚合函数,比如其他数据库中支持的聚合函数或者在用户自定义数据类型上操作的聚合函数;相关的特性是SqlUserDefinedAggregateAttribute特性,用户自定义聚合需要被序列化,以便SQL Server对他们进行存储,大多数用户自定义聚合被实现为结构体,因为结构体更加容易实现,Format.Native表示使用标准的序列化机制,只适用于简单数据类型;对于复杂数据类型,需要指定用户自定义序列化方式UserDefined;IsInvariantToDuplicates是说聚合是否和重复值无关,比如MAX、MIN聚合只会查看唯一值,不会查询重复值,而SUM则重复值也需要查看;IsInvariantToNulls是说聚合是否和NULL值无关,比如MAX就和NULL值无关,而COUNT(*)则和NULL值有关;IsNullIfEmpty是说如果没有行需要处理的话,是否不需要调用聚合直接返回NULL
- 用户自定义类型扩展了SQL Server的数据类型系统,T-SQL无法创建用自定义类型,用户自定义类型需要被序列化,以便SQL Server进行存储;相关的特性是SqlUserDefinedType;geometry、geography、hierarchyid这个系统数据类型实际上CLR 数据类型,不过他们和clr enabled配置项无关,该配置项只应用于用户自定义类型
- 用户自定义数据类型不能进行使用>、<进行比较,意味着他们不能被排序、使用在SELECT DISTINCT语句中或者直接进行索引,只可以在SqlUserDefinedAggregateAttribute的IsByteOrdered设置为true时进行简单的二进制操作;不可以在用户自定义类型上创建索引,系统托管数据类型使用的是专门的索引,只可以在同样的表上建立持久化计算列,该列使用用户自定义数据类型的属性计算而来,这样的列可以被索引;用户自定义数据类型不支持运算符重载