Managed Database Objects: The CLR Enters the Relational Stage
本文将对MSDN中ms-help://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDEV.v10.en/dnreal/html/realworld03112005.htm的用法进行改写,因为它针对的是Sql Server 2005 beta版本。
The following managed database objects are supported by SQL Server 2005 Beta 2:
- Scalar-valued user-defined functions (scalar UDFs)
- User-defined aggregates (UDAs)
- Table-valued user-defined functions (TVFs)
- User-defined procedures (UDPs)
- User-defined data types (UDTs)
- User-defined triggers
下面将通过一个具体实际的例子展示用法:
1> 生成测试数据
(
ID bigint NOT NULL identity (1, 1) primary key clustered,
FirstName nvarchar(50) NOT NULL,
LastName nvarchar(50) NOT NULL,
Country nvarchar(50) NOT NULL,
City nvarchar(50) NOT NULL,
ZipCode nvarchar(50) NOT NULL,
Street nvarchar(50) NOT NULL,
BirthDate smalldatetime NOT NULL,
Category int NOT NULL DEFAULT(0)
) ON [PRIMARY]
declare @CategoryCount as int
declare @PersonCount as bigint
declare @PersonCountMax as bigint
declare @CountryCount as int
declare @RowCount as int
delete Person
set @RowCount = 0
set @CategoryCount = 0
--create categories
while @CategoryCount < 10
begin
set @CategoryCount = @CategoryCount + 1
set @CountryCount = 0
--create countries in each category
while @CountryCount < 10
begin
set @CountryCount = @CountryCount + 1
set @PersonCount = 0
--create a random number of persons
set @PersonCountMax = rand() * 50
while @PersonCount < @PersonCountMax
begin
set @PersonCount = @PersonCount + 1
insert into
Person
values
(
'fn' + cast(@PersonCount as nvarchar(10)),
'ln' + cast(@PersonCount as nvarchar(10)),
'country' + cast(@CountryCount as nvarchar(10)),
'SqlCity',
'000000',
'SqlStreet',
GetDate(),
@CategoryCount
)
set @RowCount = @RowCount + 1
print @RowCount
end
end
end
Requirements:
What we have here is a very simple table storing information about people. The business requirements specify that each person has to part of a single category, identified by an integer value. One day, our boss (the suite, for those who attended Tech-Ed 2004 in Amsterdam) comes to us with the following requirement—he wants to know, for each category of persons, which are the three countries having the largest number of persons in that particular category.
2> Aggregate
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(Format.UserDefined,
IsInvariantToDuplicates=false,
IsInvariantToNulls=true,
IsInvariantToOrder=false,
IsNullIfEmpty=true,
MaxByteSize=8000
)]
public struct Aggregate_StrJoin:IBinarySerialize
{
private StringBuilder _result;
public void Init()
{
_result = new StringBuilder();
}
public void Accumulate(SqlString Value)
{
if (Value.IsNull)
{
return;
}
else
{
if (_result.Length > 0)
_result.Append(",");
_result.Append(Value.Value);
}
}
public void Merge(Aggregate_StrJoin Group)
{
_result.Append(Group._result);
}
public SqlString Terminate()
{
if (_result.Length > 0)
{
return new SqlString(_result.ToString());
}
return new SqlString("");
}
IBinarySerialize Members
}
使用方法:
select category,dbo.Aggregate_StrJoin(country) as countries from person
group by category
order by category
3> TVF
using System.Data;
using System.Collections;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction(FillRowMethodName = "readRow", TableDefinition = "category INT, countries NVARCHAR(100)", DataAccess = DataAccessKind.Read)]
public static IEnumerable TableValueFunction()
{
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("category", Type.GetType("System.Int32")));
dt.Columns.Add(new DataColumn("countries", Type.GetType("System.String")));
DataRow row;
using (SqlConnection cn = new SqlConnection())
{
cn.ConnectionString = "context connection=true";
using (SqlCommand cmd = cn.CreateCommand())
{
cmd.CommandText = "select category,country,count(*) as PersonCount from person" +
" group by category,country" +
" order by category,PersonCount desc";
cn.Open();
SqlDataReader dr = cmd.ExecuteReader();
int previousCategory = 1, currentCategory = 0;
int coutries = 0;
string coutryList = "";
while (dr.Read())
{
currentCategory = dr.GetInt32(0);
if (currentCategory != previousCategory)
{
row = dt.NewRow();
row[0] = previousCategory;
row[1] = coutryList;
dt.Rows.Add(row);
previousCategory = currentCategory;
coutries = 0;
coutryList = "";
}
if (++coutries <= 3)
{
coutryList += dr.GetString(1) + ",";
}
}
}
}
return dt.Rows;
}
public static void readRow(object row, out SqlInt32 category, out SqlString countries)
{
DataRow r = (DataRow)row;
category = Convert.ToInt32(r[0]);
countries = Convert.ToString(r[1]);
}
};
感受到,使用C#写存储过程,用户函数的好处:
1. 可方面的使用.Net类库,进行文件操作等(sql不易处理)
2. 进行逻辑复杂的查询、统计等,使用CLS可提高效率
3. 可创建自己的聚合函数