本节主要介绍使用CLR创建标量函数,表值函数和聚合函数。

所谓标量函数指的就是此函数只返回一个值。表值函数返回值是一个表。聚合函数是在select语句中使用的,用来聚合一个结果集,类似于Sum()或是Count()等内置的函数,而且真正的自定义聚合函数目前只能用CLR来实现。

下面的例子使用了SQLServer自带的pubs数据库。

1.CLR标量函数

1.1无参函数 
    /// 
    /// 标量函数,不带参数 
    /// 
    /// 
    [Microsoft.SqlServer.Server.SqlFunction( 
        DataAccess = DataAccessKind.Read, 
        IsDeterministic = true)] 
    public static SqlString UF_Scalar_SayHello() 
    { 
        string returnValue = "null"; 
        //由于程序是在SQL Server内执行,所以连接字符串写成"context connection=true"即可 
        using (SqlConnection conn = new SqlConnection("context connection=true")) 
        { 
            conn.Open(); 
            SqlCommand com = new SqlCommand("select top 1 [au_lname] from [dbo].[authors]",conn); 
            using (SqlDataReader dr = com.ExecuteReader(CommandBehavior.CloseConnection)) 
            { 
                if (dr.Read()) 
                    returnValue = dr.GetString(0);//返回au_lname 
            } 
        }

return returnValue;//返回"null” 
    }

CLR函数用Microsoft.SqlServer.Server.SqlFunction特征进行修饰。里面的参数含义为:DataAccess = DataAccessKind.Read表示可访问数据表。关于SqlFunctionAttribute的属性将附录在文章的最后。

/// 
    /// 标量函数,带参数 
    /// 
    /// 
    /// 
    [Microsoft.SqlServer.Server.SqlFunction] 
    public static SqlString UF_Scalar_SayHelloByPar(SqlString par) 
    { 
        return par; 
    }

2.CLR表值函数

表值与标量函数有些不同。因为要返回一个数据集合,所以一定要用一个填充数据的方法,在属性中用FillRowMethodName来表示,且返回值应该为IEnumerable类型。代码如下:

1.首先自定义返回类型 
public class ReturnData 
    { 
        public SqlString Name { get; set; } 
        public SqlString Password { get; set; } 
        public ReturnData(string name, string password) 
        { 
            this.Name = name; 
            this.Password = password; 
        } 
    }

2.写CLR表值函数 
[Microsoft.SqlServer.Server.SqlFunction( 
        DataAccess = DataAccessKind.Read, 
        FillRowMethodName = "FillRow_ReturnData",//这里是此函数的具体填充方法 
        IsDeterministic = true)] 
    public static IEnumerable UF_Table_GetReturnData() 
    { 
        List returnDataList = new List(); 
        returnDataList.Add(new ReturnData("a", "a")); 
        returnDataList.Add(new ReturnData("b", "b")); 
        returnDataList.Add(new ReturnData("c", "c")); 
        return returnDataList; 
    }

3.写填充方法 
public static void FillRow_ReturnData(object returnDataObj, 
                       out SqlString name, 
                       out SqlString password) 
    { 
        ReturnData item = returnDataObj as ReturnData; 
        name = ""; 
        password = ""; 
        if (item != null) 
        { 
            name = item.Name; 
            password = item.Password; 
        } 
    }

这样一个表值函数就写好了。确定有点麻烦,但是表值在某种情况下,也是不可替代的。

3.CLR聚合函数

用户自定义的CLR聚合类中必须四个函数:Init,Accumulate,Merge,Terminate。Init用户初始化,Accumulate用来实现具体的聚合算法,Merge用来执行每一次的聚合逻辑顺序,Terminate用来将聚合的结果返回。 
下面的代码显示了字符串的自定义聚合

#region Aggregation 
[Serializable] 
[StructLayout(LayoutKind.Sequential)] 
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate( 
    Format.UserDefined, 
    IsInvariantToDuplicates = false, 
    IsInvariantToNulls = true, 
    IsInvariantToOrder = false, 
    MaxByteSize=8000)] 
public class StringAgg : IBinarySerialize 

    private StringBuilder strBuffer;

public void Init() 
    { 
        strBuffer = new StringBuilder(); 
    }

public void Accumulate(SqlString str) 
    { 
        strBuffer.Append(string.Format("{0},", str)); 
    }

public void Merge(StringAgg Group) 
    { 
        Accumulate(Group.Terminate()); 
    }

public SqlString Terminate() 
    { 
        return strBuffer.ToString(); 
    }

#region IBinarySerialize Members

public void Read(System.IO.BinaryReader r) 
    { 
        strBuffer = new StringBuilder(r.ReadString()); 
    }

public void Write(System.IO.BinaryWriter w) 
    { 
        w.Write(strBuffer.ToString()); 
    }

#endregion 

#endregion;

4.创建函数的SQL脚本及调用方法 
关于CLR Assembly的创建方法前面已经讲过了,这里不再重复 
--创建函数 
create function UF_Scalar_SayHello() 
returns nvarchar(32) 
as EXTERNAL NAME CLRDemoAssemly.UserDefinedFunctions.UF_Scalar_SayHello 
go 
create function UF_Scalar_SayHelloByPar(@Par nvarchar(32)) 
returns nvarchar(32) 
as EXTERNAL NAME CLRDemoAssemly.UserDefinedFunctions.UF_Scalar_SayHelloByPar 
go 
create function UF_Table_GetReturnData() 
returns table(Name nvarchar(32),Password nvarchar(32)) 
as EXTERNAL NAME CLRDemoAssemly.UserDefinedFunctions.UF_Table_GetReturnData 
go 
create AGGREGATE StringAgg(@Par nvarchar(32)) 
returns nvarchar(max) 
EXTERNAL NAME CLRDemoAssemly.StringAgg 
go 
select dbo.UF_Scalar_SayHello() 
go 
select dbo.UF_Scalar_SayHelloByPar('Hello TJVictor') 
go 
select * from dbo.UF_Table_GetReturnData() 
go 
select dbo.StringAgg(au_lname) from dbo.authors

5.SqlFunctionAttribute的属性 

名称 说明
DataAccess 指示函数是否需要访问存储在 SQL Server 的本地实例中的用户数据。
FillRowMethodName 方法的名称,该方法与 TVF 协定所使用的表值函数 (TVF) 在同一个类中。
IsDeterministic 指示用户定义的函数是否是确定性的。
IsPrecise 指示函数是否涉及不精确的计算,如浮点运算。
Name 函数在 SQL Server 中注册时所使用的名称。
SystemDataAccess 指示函数是否需要访问存储在 SQL Server 的系统目录或虚拟系统表中的数据。
TableDefinition 如果方法用作表值函数 (TVF),则为一个字符串,该字符串表示结果的表定义。
TypeId 当在派生类中实现时,获取该 Attribute 的唯一标识符。

6.SqlUserDefinedAggregateAttribute的属性

名称 说明
Format 序列化格式为 Format 的值之一。如果选择Native,则聚合类一定要被[StructLayout(LayoutKind.Sequential)]修饰;如果选择UserDefined,则聚合类一定要继承IBinarySerialize接口,自己写序列化方法。
IsInvariantToDuplicates 指示聚合是否与重复值无关。
IsInvariantToNulls 指示聚合是否与空值无关。
IsInvariantToOrder 指示聚合是否与顺序无关。
IsNullIfEmpty 指示在没有对任何值进行累积时聚合是否返回空引用。
MaxByteSize 聚合实例的最大大小。
Name 聚合的名称。
TypeId 当在派生类中实现时,获取该 Attribute 的唯一标识符。

7.附录完整程序

  1. using System;
  2. using System.Data;
  3. using System.Data.SqlClient;
  4. using System.Data.SqlTypes;
  5. using Microsoft.SqlServer.Server;
  6. using System.Collections;
  7. using System.Collections.Generic;
  8. using System.Text;
  9. using System.Runtime.InteropServices;
  10. public partial class UserDefinedFunctions
  11. {
  12. #region Scalar
  13. /// <summary>
  14. /// 标量函数,不带参数
  15. /// </summary>
  16. /// <returns></returns>
  17. [Microsoft.SqlServer.Server.SqlFunction(
  18. DataAccess = DataAccessKind.Read)]
  19. public static SqlString UF_Scalar_SayHello()
  20. {
  21. string returnValue = "null";
  22. //由于程序是在SQL Server内执行,所以连接字符串写成"context connection=true"即可
  23. using (SqlConnection conn = new SqlConnection("context connection=true"))
  24. {
  25. conn.Open();
  26. SqlCommand com = new SqlCommand("select top 1 [au_lname] from [dbo].[authors]", conn);
  27. using (SqlDataReader dr = com.ExecuteReader(CommandBehavior.CloseConnection))
  28. {
  29. if (dr.Read())
  30. returnValue = dr.GetString(0);
  31. }
  32. }
  33. return returnValue;
  34. }
  35. /// <summary>
  36. /// 标量函数,带参数
  37. /// </summary>
  38. /// <param name="par"></param>
  39. /// <returns></returns>
  40. [Microsoft.SqlServer.Server.SqlFunction]
  41. public static SqlString UF_Scalar_SayHelloByPar(SqlString par)
  42. {
  43. return par;
  44. }
  45. #endregion
  46. #region Table
  47. /// <summary>
  48. /// 表值函数。
  49. /// </summary>
  50. /// <returns></returns>
  51. [Microsoft.SqlServer.Server.SqlFunction(
  52. DataAccess = DataAccessKind.Read,
  53. FillRowMethodName = "FillRow_ReturnData",
  54. IsDeterministic = true)]
  55. public static IEnumerable UF_Table_GetReturnData()
  56. {
  57. List<ReturnData> returnDataList = new List<ReturnData>();
  58. returnDataList.Add(new ReturnData("a", "a"));
  59. returnDataList.Add(new ReturnData("b", "b"));
  60. returnDataList.Add(new ReturnData("c", "c"));
  61. return returnDataList;
  62. }
  63. public class ReturnData
  64. {
  65. public SqlString Name { get; set; }
  66. public SqlString Password { get; set; }
  67. public ReturnData(string name, string password)
  68. {
  69. this.Name = name;
  70. this.Password = password;
  71. }
  72. }
  73. public static void FillRow_ReturnData(object returnDataObj,
  74. out SqlString name,
  75. out SqlString password)
  76. {
  77. ReturnData item = returnDataObj as ReturnData;
  78. name = "";
  79. password = "";
  80. if (item != null)
  81. {
  82. name = item.Name;
  83. password = item.Password;
  84. }
  85. }
  86. #endregion
  87. };
  88. #region Aggregation
  89. [Serializable]
  90. [StructLayout(LayoutKind.Sequential)]
  91. [Microsoft.SqlServer.Server.SqlUserDefinedAggregate(
  92. Format.UserDefined,
  93. IsInvariantToDuplicates = false,
  94. IsInvariantToNulls = true,
  95. IsInvariantToOrder = false,
  96. MaxByteSize=8000)]
  97. public class StringAgg : IBinarySerialize
  98. {
  99. private StringBuilder strBuffer;
  100. public void Init()
  101. {
  102. strBuffer = new StringBuilder();
  103. }
  104. public void Accumulate(SqlString str)
  105. {
  106. strBuffer.Append(string.Format("{0},", str));
  107. }
  108. public void Merge(StringAgg Group)
  109. {
  110. Accumulate(Group.Terminate());
  111. }
  112. public SqlString Terminate()
  113. {
  114. return strBuffer.ToString();
  115. }
  116. #region IBinarySerialize Members
  117. public void Read(System.IO.BinaryReader r)
  118. {
  119. strBuffer = new StringBuilder(r.ReadString());
  120. }
  121. public void Write(System.IO.BinaryWriter w)
  122. {
  123. w.Write(strBuffer.ToString());
  124. }
  125. #endregion
  126. }
  127. #endregion;

CLR系列文章链接:
SQL Server CLR全功略之一---CLR介绍和配置:
http://blog.csdn.net/tjvictor/archive/2009/10/25/4726933.aspx

SQL Server CLR全功略之二---CLR存储过程:
http://blog.csdn.net/tjvictor/archive/2009/10/26/4731052.aspx

SQL Server CLR全功略之三---CLR标量函数、表值函数和聚合函数(UDA):
http://blog.csdn.net/tjvictor/archive/2009/11/10/4793781.aspx

SQL Server CLR全功略之四---CLR触发器:
http://blog.csdn.net/tjvictor/archive/2009/11/10/4795569.aspx

SQL Server CLR全功略之五---CLR自定义数据类型

http://blog.csdn.net/tjvictor/archive/2009/11/13/4807901.aspx

如需转载,请注明本文原创自CSDN TJVictor专栏:http://blog.csdn.net/tjvictor

转载——CLR标量函数、表值函数和聚合函数(UDA)的更多相关文章

  1. Spark SQL 用户自定义函数UDF、用户自定义聚合函数UDAF 教程(Java踩坑教学版)

    在Spark中,也支持Hive中的自定义函数.自定义函数大致可以分为三种: UDF(User-Defined-Function),即最基本的自定义函数,类似to_char,to_date等 UDAF( ...

  2. SQL语句函数详解__sql聚合函数

    函数是一种有零个或多个参数并且有一个返回值的程序.在SQL中Oracle内建了一系列函数,这些函数都可被称为SQL或PL/SQL语句,函数主要分为两大类:单行函数.组函数 本文将讨论如何使用单行函数及 ...

  3. ORACLE字符串分组聚合函数(字符串连接聚合函数)

    ORACLE字符串连接分组串聚函数 wmsys.wm_concat SQL代码: select grp, wmsys.wm_concat(str) grp, 'a1' str from dual un ...

  4. 【MySQL作业】avg 和 count 函数——美和易思聚合函数应用习题

    点击打开所使用到的数据库>>> 1.统计所有商品的平均单价.最高单价与平均单价之差.平均单价与最低单价之差. 最高单价与平均单价之差 = max(unitPrice)-avg(uni ...

  5. 【MySQL作业】sum、max 和 min 聚合函数——美和易思聚合函数应用习题

    点击打开所使用到的数据库>>> 1.统计商品最高单价和最低单价. -- 获取所有商品的最高单价和最低单价: select max(unitPrice) 最高单价 , min(unit ...

  6. SQL语言基本操作(聚合函数)

    一.聚合函数 1.标量函数:只能对单个的数字或值进行计算.主要包括字符函数.日期/时间函数.数值函数和转换函数这四类.如LEFT/RIGHT/SUBSTRING/LTRIM/RTRIM/CONCAT/ ...

  7. sqlserver的over开窗函数(与排名函数或聚合函数一起使用)

    首先初始化表和数据 create table t_student(   Id INT,   Name varchar(),   Score int,   ClassId INT ); insert i ...

  8. 10-31SQLserver基础--聚合函数、分组

    在查询语句时,也存在一些方法和属性,而这些方法在查询时统称为函数,便利查询时使用 聚合函数(都是针对字段操作) 聚合是缩减一系列输入值的表达式,例如缩减为单个值. Select*from biao 1 ...

  9. MySQL数据库:聚合函数的使用

    聚合函数 max() 最大值 min() 最小值 avg() 平均值 sum() 求和 count() 符合条件数据的数目 聚合函数不能嵌套使用 # 在统计时字段内没有满足条件的数值只有count返回 ...

随机推荐

  1. oracle sql语句中使用if逻辑

    l在 SQL 语句中使用IF-THEN-ELSE 逻辑 l l使用两种方法: •CASE 表达式:SQL99的语法,类似Basic,比较繁琐 •DECODE 函数:Oracle自己的语法,类似Java ...

  2. C++拾遗(六)函数相关(1)

    返回值 C++规定返回值不能是 数组.但可以是其它任何类型(包括结构体和对象). 通常,函数将返回值复制到指定的CPU寄存器或内存单元中,然后调用函数调用该内存单元的值. 函数原型 参数列表中可以不包 ...

  3. 【转载】经典10道c/c++语言经典笔试题(含全部所有参考答案)

    经典10道c/c++语言经典笔试题(含全部所有参考答案) 1. 下面这段代码的输出是多少(在32位机上). char *p; char *q[20]; char *m[20][20]; int (*n ...

  4. 网易JS面试题与Javascript词法作用域说明

    调用对象位于作用域链的前端,局部变量(在函数内部用var声明的变量).函数参数及Arguments对象都在函数内的作用域中--这意味着它们隐藏了作用域链更上层的任何同名的属性. 2010年9月14日, ...

  5. 使用微软 AppFabric 遇到问题

    我做的一个项目用了,但是遇到很奇怪的问题,在测试环境下,两台机做集群,一切正常,达到设计要求,但是部署到专用网络(内部网络,无法访问internet),老是提示访问服务器超时,初步排查,发现貌似是域的 ...

  6. ECSTORE2.0 去页面底部版权

    ECstore系统在每个页面底部都有版权信息,非常烦人,之前的解决方法是修改系统代码,但是对不懂的php代码人来说是个很困扰的事情. 现在ECStore在版本为2.0.32中进行了代码更新,只需要在c ...

  7. phpcms v9 数据库分离部署

    v9数据模型功能,允许用户把不同的数据表,分离到不同的数据库服务器上.以实现负载的分离,更加的符合大访问网站的需求. <ignore_js_op> 数据分离方法 1.数据库连接配置配置文件 ...

  8. nginx配置使其支持thinkphp的pathinfo模式

    #user root;#user nobody;worker_processes 1; #error_log logs/error.log;#error_log logs/error.log noti ...

  9. centos6.2下安装星际译王stardict3.0

    星际译王是一个Linux下很好的翻译软件. 我的系统是centos6.2 32位版.本来在http://code.google.com/p/stardict-3/downloads/list 上下的源 ...

  10. 猜数字-js

    var n = Math.round(Math.random()*10); //随机数 // alert(n); while(true){ var Onum = prompt('请输入1-10之间的数 ...