演练:调试 SQL CLR 用户定义的聚合
本主题适用于:
版本 |
Visual Basic |
C# |
C++ |
Web Developer |
---|---|---|---|---|
学习版 |
||||
标准版 |
||||
专业及团队版 |
本示例演示如何调试 SQL Server 公共语言运行时 (SQL CLR) 用户定义的聚合。 它在**“AdventureWorks”示例数据库中创建一个新的名为“Concatenate”**的 SQL CLR 聚合函数。 当在 SQL 语句中调用此函数时,它将作为其输入参数的指定的列的所有值串联在一起。
如果在尝试调试 SQL CLR 对象时,显示消息“被用户取消”,您必须手动配置运行 Visual Studio 的计算机以及运行 SQL Server 的计算机。 有关更多信息,请参见如何:配置计算机以启用 Transact-SQL 和 SQL CLR 调试。
提示
显示的对话框和菜单命令可能会与“帮助”中的描述不同,具体取决于您现用的设置或版本。 若要更改设置,请在“工具”菜单上选择“导入和导出设置”。 有关更多信息,请参见 使用设置。
调试 CLR SQL 聚合函数
在一个新的 SQL CLR 项目中,建立一个到**“AdventureWorks”**示例数据库的连接。 有关更多信息,请参见How to: Connect to a Database。
使用以下示例部分中第一个示例的代码创建一个新的函数,并将其命名为“Concatenate.cs”。 有关更多信息,请参见How to: Develop with the SQL Server Project Type。
添加一个对该功能进行测试的脚本,方法是,在 SELECT 语句中包括该脚本。 在**“解决方案资源管理器”中右击“TestScripts”目录,选择“添加测试脚本”,然后插入本演练中第二个示例部分的代码。 以“Concatenate.sql”名称保存文件。 右击该文件名,然后单击“设置为默认调试脚本”**。
将断点放置在 Concatenate.cs 中的 Accumulate 方法内的 if 语句上。 若要执行此操作,请单击“文本编辑器”窗口左侧的灰色空白处,然后在**“调试”菜单上,单击“启动”**,对项目执行编译、部署和单元测试。 以黄色箭头表示的指令指针出现在断点上时,说明正在调试函数。
尝试各种调试功能。
对于 Concatenate.sql 中构成脚本的 GROUP BY 子句的每一行,都会执行一次 Accumulate 方法。 通过在**“调试”菜单上重复单击“逐语句”**,可监视方法结果的生成方式。
在**“局部变量”**窗口中,打开变量 value,它包含当前正处理的存储区名称。
单击变量 this。 此函数将返回子节点 intermediateResult,该节点包含到当前存储名为止的所有存储名,这些存储名连接在一起并以逗号分隔。
在文本编辑器中,双击 intermediateResult 变量以选择它。 将 intermediateResult 拖动到**“监视”**窗口,并将它放在窗口中的任意位置。 该变量随即添加到受监视的变量列表中。
逐句通过方法几次。 每次通过该方法时,intermediateResult 的值都将更改,并在结尾处连接另外一个存储名。
单击断点以将其移除,然后向 Terminate 方法内的第一个语句添加断点。 此方法将结果返回给调用方。 若要单步执行它,请在**“调试”菜单上,单击“启动”。 现在可以通过单击“调试”菜单上的“单步执行”**来逐句通过它。 在遇到 return 语句时停止。
再次单击**“继续”**完成函数调试。
示例
下面是本示例中使用的聚合函数的代码。
using System;
using System.Data.Sql;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.IO;
using System.Text;
[Serializable]
[SqlUserDefinedAggregate(
//use CLR serialization to serialize the intermediate result.
Format.UserDefined,
//Optimizer property:
IsInvariantToNulls=true,
//Optimizer property:
IsInvariantToDuplicates=false,
//Optimizer property:
IsInvariantToOrder=false,
//Maximum size in bytes of persisted value:
MaxByteSize=8000)
]
public class Concatenate: IBinarySerialize
{
/// <summary>
/// Variable holds intermediate result of the concatenation
/// </summary>
private StringBuilder intermediateResult;
/// <summary>
/// Initialize the internal data structures
/// </summary>
public void Init( )
{
intermediateResult = new StringBuilder();
}
/// <summary>
/// Accumulate the next value, nop if the value is null
/// </summary>
/// <param name="value"></param>
public void Accumulate(SqlString value)
{
if(value.IsNull)
{
return;
}
intermediateResult.Append(value.Value).Append(',');
}
/// <summary>
/// Merge the partially computed aggregate with this aggregate.
/// </summary>
/// <param name="other"></param>
public void Merge( Concatenate other)
{
intermediateResult.Append(other.intermediateResult);
}
/// <summary>
/// Called at end of aggregation, to return results.
/// </summary>
/// <returns></returns>
public SqlString Terminate()
{
string output = string.Empty;
//Delete the trailing comma, if any .
if (intermediateResult != null && intermediateResult.Length > 0)
output = intermediateResult.ToString(0, intermediateResult.Length-1);
return new SqlString(output);
}
public void Read(BinaryReader r)
{
intermediateResult = new StringBuilder(r.ReadString());
}
public void Write(BinaryWriter w)
{
w.Write(intermediateResult.ToString());
}
}
这是调用此函数的测试脚本。
SELECT scu.SalesPersonID, dbo.Concatenate(sst.Name)
FROM Sales.Customer as scu
INNER JOIN Sales.Store as sst
ON scu.CustomerID = sst.CustomerID
INNER JOIN Sales.SalesPerson as spr
ON scu.SalesPersonID = spr.SalesPersonID
WHERE scu.SalesPersonID = 283
GROUP BY scu.SalesPersonID