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> 生成测试数据

create table Person
(
   ID 
bigint NOT NULL identity (11primary key clustered,
   FirstName 
nvarchar(50NOT NULL,
   LastName 
nvarchar(50NOT NULL,
   Country 
nvarchar(50NOT NULL,
   City 
nvarchar(50NOT NULL,
   ZipCode 
nvarchar(50NOT NULL,
   Street 
nvarchar(50NOT 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;
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;
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. 可创建自己的聚合函数

posted @ 2007-08-03 11:44  Ready!  阅读(1212)  评论(0编辑  收藏  举报