一、简介

Microsoft SQL Server 2005之后,实现了对 Microsoft .NET Framework 的公共语言运行时(CLR)的集成。
CLR 集成使得现在可以使用 .NET Framework 语言编写代码,从而能够在 SQL Server 上运行,现在就可以通过 C# 来编写 SQL Server 自定义函数、存储过程、触发器等。
我最初的目的是因为在 SQL Server 数据库中遇到数字的十进制与十六进制的互相转换问题,也看过一些方法吧,但是最后我却选择了用 CLR 来做,毕竟在 C# 中两三行代码就能搞定的问题。。。

二、配置 SQL Server CLR

开启 CLR:

--开启所有服务器配置
sp_configure 'show advanced options', 1;
RECONFIGURE WITH override
GO
--开启 CLR
sp_configure 'clr enabled', 1;
RECONFIGURE WITH override
GO

  

关闭 CLR:

--关闭所有服务器配置
sp_configure 'show advanced options', 0;
RECONFIGURE WITH override
GO
--关闭 CLR
sp_configure 'clr enabled', 0;
RECONFIGURE WITH override
GO

  

在后面注册 CLR 程序集时,发生因操作权限问题而导致的失败时,可以尝试执行下面的 SQL 语句,这里我把 SQL 一并贴出来。

--权限不够时,设置目标数据库为可信赖的,例如:Test
ALTER DATABASE [Test] SET TRUSTWORTHY ON --修改数据库所有者为当前登录的用户,也可以为其他用户,例如:sa
EXEC sp_changedbowner 'sa'

三、CLR Function

打开 Visual Studio 新建一个 SQL Server 数据库项目,这里需要注意 .NET Framework 的版本。
因为我的目标数据库为 SQL Server 2008,所以这里我选择的是 .NET Framework 3.5 的版本。
然后添加新建项,选择 SQL CLR C# 用户自定义函数,先从标量函数开始。

1、标量函数

public partial class UserDefinedFunctions
{
/// <summary>
/// 10进制转16进制
/// </summary>
/// <param name="strNumber"></param>
/// <returns></returns>
[Microsoft.SqlServer.Server.SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, Name = "ConvertToHexadecimal")]
public static SqlString ConvertToHexadecimal(SqlString strNumber)
{
SqlString result = string.Empty;
string str = strNumber.ToString();
int number = 0;
if (int.TryParse(str, out number))
{
result = number.ToString("X");
}
return result;
} /// <summary>
/// 16进制转10进制
/// </summary>
/// <param name="strNumber"></param>
/// <returns></returns>
[Microsoft.SqlServer.Server.SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, Name = "ConvertToDecimal")]
public static SqlString ConvertToDecimal(SqlString strNumber)
{
SqlString result = string.Empty;
string str = strNumber.ToString();
int number = 0;
try
{
number = int.Parse(str, System.Globalization.NumberStyles.HexNumber);
result = Convert.ToString(number, 10);
}
catch
{
}
return result;
}
}

  

2、表值函数

public partial class UserDefinedFunctions
{
/// <summary>
/// SQL Server 字符串分割方法
/// </summary>
/// <param name="separator"></param>
/// <param name="pendingString"></param>
/// <returns></returns>
[Microsoft.SqlServer.Server.SqlFunction(
DataAccess = DataAccessKind.Read,
IsDeterministic = true,
Name = "SqlSplit",
FillRowMethodName = "SqlSplit_FillRow",
TableDefinition = "SerialNumber int,StringValue nvarchar(1024)")]
public static IEnumerable SqlSplit(SqlString separator, SqlString pendingString)
{
string _separator = string.Empty;
string _pendingString = string.Empty;
if (separator.IsNull)
{
_separator = ",";
}
else
{
_separator = separator.ToString();
if (string.IsNullOrEmpty(_separator))
{
_separator = ",";
}
} if (pendingString.IsNull)
{
return null;
}
else
{
_pendingString = pendingString.ToString();
if (string.IsNullOrEmpty(_pendingString))
{
return null;
}
} string[] strs = _pendingString.Split(new string[] { _separator }, StringSplitOptions.RemoveEmptyEntries);
if (strs.Length <= 0)
{
return null;
} List<ResultData> resultDataList = new List<ResultData>();
for (int i = 0; i < strs.Length; i++)
{
resultDataList.Add(new ResultData(i + 1, strs[i]));
}
return resultDataList;
} /// <summary>
/// 填充数据方法
/// </summary>
/// <param name="obj"></param>
/// <param name="serialNumber"></param>
/// <param name="stringValue"></param>
public static void SqlSplit_FillRow(Object obj, out SqlInt32 SerialNumber, out SqlString StringValue)
{
ResultData resultData = (ResultData)obj;
SerialNumber = resultData.SerialNumber;
StringValue = resultData.StringValue;
} /// <summary>
/// 定义返回类型
/// </summary>
public class ResultData
{
/// <summary>
/// 序号,即行号
/// </summary>
public SqlInt32 SerialNumber { get; set; } /// <summary>
/// 分割后的每个子字符串
/// </summary>
public SqlString StringValue { get; set; } public ResultData(SqlInt32 serialNumber, SqlString stringValue)
{
SerialNumber = serialNumber;
StringValue = stringValue;
}
}
}

  

SqlFunctionAttribute 的属性及介绍:

--属性                    --说明
--DataAccess --指示该函数是否涉及访问存储在SQL Server的数据
--FillRowMethodName --在同一个类的方法的名称作为表值函数(TVF),这个参数在表值函数中才会用到,用于指定表值函数的数据填充方法
--IsDeterministic --指示用户定义的函数是否是确定性的
--IsPrecise --指示函数是否涉及不精确计算,如浮点运算
--Name --函数在SQL Server中注册时使用的函数的名称
--SystemDataAccess --指示该函数是否需要访问存储在系统目录或SQL Server虚拟系统表中的数据
--TableDefinition --如果方法作为表值函数(TVF),则为一个字符串,该字符串表示表结构的定义

  

标量函数与表值函数可以写在同一个类文件里面,并且可以包含多个,但是聚合函数就不行了,现在需要添加一个新项,选择 SQL CLR C# 聚合。

3、聚合函数

我这里写的这个聚合函数的作用是把多个字符串拼为一个字符串,我之前还真有遇到这种情况需要的。

[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(
Format.UserDefined,
IsInvariantToDuplicates = false,
IsInvariantToNulls = true,
IsInvariantToOrder = false,
MaxByteSize = 8000,
Name = "SumString")]
public struct UserDefinedSqlAggregate : IBinarySerialize
{
private StringBuilder stringBuilder; /// <summary>
/// 查询处理器使用此方法初始化聚合的计算
/// </summary>
public void Init()
{
stringBuilder = new StringBuilder();
} /// <summary>
/// 查询处理器使用此方法累计聚合值
/// </summary>
/// <param name="Value"></param>
public void Accumulate(SqlString Value)
{
stringBuilder.Append(string.Format("{0},", Value));
} /// <summary>
/// 查询处理器使用此方法合并聚合的多个部分计算的值
/// </summary>
/// <param name="Group"></param>
public void Merge(UserDefinedSqlAggregate Group)
{
stringBuilder.Append(Group.stringBuilder);
} /// <summary>
/// 此方法用于返回完成聚合计算的结果
/// </summary>
/// <returns></returns>
public SqlString Terminate()
{
return new SqlString(stringBuilder.ToString());
} #region Implement interface IBinarySerialize
/// <summary>
/// 读
/// </summary>
/// <param name="r"></param>
public void Read(System.IO.BinaryReader r)
{
stringBuilder = new StringBuilder(r.ReadString());
} /// <summary>
/// 写
/// </summary>
/// <param name="w"></param>
public void Write(System.IO.BinaryWriter w)
{
w.Write(stringBuilder.ToString());
}
#endregion
}

  

SqlUserDefinedAggregateAttribute 的属性及介绍:

--属性                        --说明
--Format --选择序列化的 Format 格式,默认选择 Native,表示使用本地序列化格式。如果选择 UserDefined,则聚合类需要实现 IBinarySerialize 接口
--IsInvariantToDuplicates --指示聚合是否与重复的值相计算保持不变
--IsInvariantToNulls --指示聚合是否与空值相计算保持不变
--IsInvariantToOrder --指示聚合最后计算的结果是否与顺序无关
--IsNullIfEmpty --指示在没有对任何值进行累计时,聚合返回值是否为 null
--MaxByteSize --聚合实例的最大大小(以字节为单位)
--Name --聚合函数的名称

  

然后生成项目,接下来注册程序集和注册函数就可以使用了。

4、注册 CLR 程序集

注册程序集的方式有以下两种:

第一种,这种方式注册程序集比较简单,但是缺点就是程序集不能移动或删除。

--注册CLR程序集方式一,指定程序集DLL的路径
USE Test
GO
CREATE ASSEMBLY UserDefinedClrAssembly
--AUTHORIZATION sa --指定数据库所有者,默认为当前用户
FROM 'C:\Users\Administrator\Desktop\CLR Assembly\UserDefinedSqlClr.dll' --指定文件路径
WITH PERMISSION_SET = UNSAFE; --指定程序集的权限
--SAFE:无法访问外部系统资源;
--EXTERNAL_ACCESS:可以访问某些外部系统资源;
--UNSAFE:可以不受限制的访问外部系统资源
GO

  

这里如果发生因为程序集拒绝访问的错误,那就把计算机用户 Everyone 的权限改为完全控制就可以了。

第二种,这种方式注册程序集稍微复杂一些,但是好处就是注册成功之后,可以移动甚至删除DLL文件,只要不是变更迁移数据库,都不用重新注册。

--注册CLR程序集方式二,指定程序集DLL的16进制文件流
USE Test
GO
CREATE ASSEMBLY UserDefinedClrAssembly
--AUTHORIZATION sa --指定数据库所有者,默认为当前用户
FROM 0x4D5A90000300000004000000FFFF0000B8000000000000004000000000 --指定DLL的16进制文件流(当然没这么少,我删掉了)
WITH PERMISSION_SET = UNSAFE; --指定程序集的权限
--SAFE:无法访问外部系统资源;
--EXTERNAL_ACCESS:可以访问某些外部系统资源;
--UNSAFE:可以不受限制的访问外部系统资源
GO

  

获取DLL的16进制文件流,可以使用 UltraEdit 这个软件,具体操作方法这里就不多说了。

注册成功之后,可以使用下面的 SQL 语句查看程序集的信息,还包括查询自定义的函数、存储过程等的SQL语句,这个下面注册函数之后可以用到。

--查看程序集信息
SELECT * FROM sys.assemblies --查看模块信息,即自定义函数、视图、存储过程、触发器等等
SELECT * FROM sys.sql_modules
GO

5、注册函数

下面是三种函数的注册方式的 SQL 语句。

USE Test
GO --注册标量函数 ConvertToHexadecimal
CREATE FUNCTION [dbo].[ConvertToHexadecimal](@strNumber NVARCHAR(128))
RETURNS NVARCHAR(128)
WITH EXECUTE AS CALLER --用于在用户在执行函数的时候对引用的对象进行权限检查
AS
EXTERNAL NAME [UserDefinedClrAssembly].[UserDefinedFunctions].[ConvertToHexadecimal] --EXTERNAL NAME 程序集名.类名.方法名
GO --注册标量函数 ConvertToDecimal
CREATE FUNCTION [dbo].[ConvertToDecimal](@strNumber NVARCHAR(128))
RETURNS NVARCHAR(128)
WITH EXECUTE AS CALLER --用于在用户在执行函数的时候对引用的对象进行权限检查
AS
EXTERNAL NAME [UserDefinedClrAssembly].[UserDefinedFunctions].[ConvertToDecimal] --EXTERNAL NAME 程序集名.类名.方法名
GO --注册表值函数 SqlSplit
CREATE FUNCTION [dbo].[SqlSplit](@separator NVARCHAR(32),@string NVARCHAR(MAX))
RETURNS TABLE
(
SerialNumber INT,
StringValue NVARCHAR(1024)
)
WITH EXECUTE AS CALLER --用于在用户在执行函数的时候对引用的对象进行权限检查
AS
EXTERNAL NAME [UserDefinedClrAssembly].[UserDefinedFunctions].[SqlSplit] --EXTERNAL NAME 程序集名.类名.方法名
GO --注册聚合函数 SumString
CREATE AGGREGATE [dbo].[SumString](@params NVARCHAR(128))
RETURNS NVARCHAR(MAX)
EXTERNAL NAME [UserDefinedClrAssembly].[UserDefinedSqlAggregate] --EXTERNAL NAME 程序集名.类名
GO

  

注册函数成功之后,接下来测试一下。

DECLARE @TempTable TABLE
(
Id INT NOT NULL,
Name NVARCHAR(32) NOT NULL
)
INSERT INTO @TempTable (
Id,
[Name]
)
SELECT '1','小张' UNION ALL
SELECT '2','小明' UNION ALL
SELECT '2','小丽' UNION ALL
SELECT '2','小李' UNION ALL
SELECT '3','小王' UNION ALL
SELECT '3','小舞' SELECT dbo.ConvertToHexadecimal('15') SELECT dbo.ConvertToDecimal('FC') SELECT * FROM SqlSplit(',',',123,456,789,') SELECT Id,dbo.SumString([Name]) Names
FROM @TempTable
GROUP BY Id

  

结果如图。

下面是删除函数和删除程序集的 SQL 语句,虽然可能用不到,但是还是贴出来吧。

这里需要注意的是,删除程序集时要保证不存在函数、存储过程、触发器等对程序集的引用。

--删除标量函数 ConvertToHexadecimal
DROP FUNCTION dbo.ConvertToHexadecimal --删除标量函数 ConvertToDecimal
DROP FUNCTION dbo.ConvertToDecimal --删除表值函数 SqlSplit
DROP FUNCTION dbo.SqlSplit --删除聚合函数 SumString
DROP FUNCTION dbo.SumString --删除程序集 UserDefinedClrAssembly
DROP ASSEMBLY UserDefinedClrAssembly

  

本想一篇写完的,还是算了,存储过程和触发器留待下一篇。

其实存储过程和触发器也没什么了,只是 C# 代码不一样而已,其他注册之类的大同小异。

这里推荐一篇博客,大家也可以去看这篇,写得还是挺完整的,有些地方都是借鉴于此。

http://blog.csdn.net/tjvictor/article/details/4726933

转载自:http://www.cnblogs.com/Brambling/p/8000911.html

SQL Server CLR 使用 C# 自定义函数的更多相关文章

  1. SQL Server CLR 使用 C# 自定义存储过程和触发器

    资源来源:https://www.cnblogs.com/Brambling/p/8016060.html SQL Server CLR 使用 C# 自定义存储过程和触发器   这一篇博客接着上一篇博 ...

  2. SQL Server技术问题之自定义函数优缺点

    优点: 可以在SQL语句中调用,直接使用返回值,从而可以形成复杂的SQL应用. 缺点: 能在函数中使用的语句有严格限制: 不支持create.ALTER.drop等DDL(Data Definitio ...

  3. SQL SERVER中用户定义标量函数(scalar user defined function)的性能问题

    用户定义函数(UDF)分类  SQL SERVER中的用户定义函数(User Defined Functions 简称UDF)分为标量函数(Scalar-Valued Function)和表值函数(T ...

  4. SQL Server利用RowNumber()内置函数与Over关键字实现通用分页存储过程(支持单表或多表结查集分页)

    SQL Server利用RowNumber()内置函数与Over关键字实现通用分页存储过程,支持单表或多表结查集分页,存储过程如下: /******************/ --Author:梦在旅 ...

  5. (转载)MS SQL Server 未公开的加密函数有哪些?

    MS SQL Server 未公开的加密函数有哪些? 以下的文章是对MS SQL Server 未公开的加密函数的具体操作,如果你对其相关的实际操作有兴趣的话,你就可以点击了. MS SQL Serv ...

  6. SQL SERVER 提供了一些时间函数:

    SQL SERVER 提供了一些时间函数:取当前时间:select getdate()取前一个月的时间:SELECT DATEADD(MONTH,-1,GETDATE()) 月份减一个月取年份:SEL ...

  7. 深入理解SQL Server 2005 中的 COLUMNS_UPDATED函数

    原文:深入理解SQL Server 2005 中的 COLUMNS_UPDATED函数 概述 COLUMNS_UPDATED函数能够出现在INSERT或UPDATE触发器中AS关键字后的任何位置,用来 ...

  8. SQL Server 2019 中标量用户定义函数性能的改进

    在SQL Server中,我们通常使用用户定义的函数来编写SQL查询.UDF接受参数并将结果作为输出返回.我们可以在编程代码中使用这些UDF,并且可以快速编写查询.我们可以独立于任何其他编程代码来修改 ...

  9. SQL Server CLR全功略之一---CLR介绍和配置

    Microsoft SQL Server 现在具备与 Microsoft Windows .NET Framework 的公共语言运行时 (CLR) 组件集成的功能.CLR 为托管代码提供服务,例如跨 ...

随机推荐

  1. localtime 和 localtime_r

    #include <cstdlib> #include <iostream> #include <time.h> #include <stdio.h> ...

  2. javascript基础拾遗(七)

    1.对象的继承__proto__ var Language = { name: 'program', score: 8.0, popular: function () { return this.sc ...

  3. python(43):collections模块

    Python作为一个“内置电池”的编程语言,标准库里面拥有非常多好用的模块.比如今天想给大家 介绍的 collections 就是一个非常好的例子. 基本介绍: 我们都知道,python拥有一些内阻的 ...

  4. 此编译单元不包含在frame元数据中指定的factoryClass,无法加载配置的运行时共享库

    警告:此编译单元不包含在frame元数据中指定的factoryClass,无法加载配置的运行时共享库.要在没有运行时共享库的情况下进行编译,请将 -static-link-runtime-shared ...

  5. android alipay

    "java.security.spec.InvalidKeySpecException" KeyFactory keyFactory =KeyFactory.getInstance ...

  6. 【ARM】2440裸机系列-图片显示

    功能 LCD显示字汉字,字符和图片 说明 汉字,字符和图片需要用相应的取模软件得到相应的c文件,然后包含到工程中 主要代码   1)绘制背景 void Brush_ U32 c) { int x,y ...

  7. SecurityError: Blocked a frame with origin from accessing a cross-origin frame

    问题描述:浏览器报错I am loading an <iframe> in my HTML page and trying to access the elements within it ...

  8. LeetCode: Search a 2D Matrix 解题报告

    Search a 2D Matrix Write an efficient algorithm that searches for a value in an m x n matrix. This m ...

  9. 设计模式之观察者模式(关于OC中的KVO\KVC\NSNotification)

    学习了这么久的设计模式方面的知识,最大的感触就是,设计模式不能脱离语言特性.近段时间所看的两本书籍,<大话设计模式>里面的代码是C#写的,有一些设计模式实现起来也是采用了C#的语言特性(C ...

  10. plot sin 动态配置rc settings

    plot sin 动态配置rc settings 坐标轴颜色 线的颜色 绘图前景色 Code #!/usr/bin/env python # -*- coding: utf-8 -*- import ...