随笔 - 99  文章 - 2 评论 - 902 trackbacks - 17
<2008年7月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789


转载请保留链接。
国家体育场(鸟巢)是建筑史上的令人惊叹之作,杂乱的基础结构组成了一个和谐的整体,正是中国社会的成功写照。 7-18 17:44

与我联系

常用链接

留言簿(12)

我参与的团队

我的标签

随笔分类

随笔档案

文章分类

相册

最新随笔

搜索

  •  

积分与排名

  • 积分 - 141508
  • 排名 - 243

最新评论

阅读排行榜

评论排行榜

60天内阅读排行

 在我们需要批量删除数据,或者批量修改实体的状态时,为了性能我们会直接写一个存储过程,并将这一批数据的id用“,”分隔传递给一个存储过程,然后在存储过程中拆分这个字符串,然后执行删除或者更新状态操作。以前每次执行这种操作时我都会在存储过程中拆分字符串。现在sql server支持用.net clr的程序集写函数,存储过程等等。 

现在我们就牛刀小试,做一个clrsql表值函数。该函数的功能就是传入一个用逗号分隔的数字id字符,返回一个只有一列id的表。
第一步:我们需要新建一个类库项目,并添加一个类SplitIDs

using System;
using System.Collections.Generic;
using System.
Text;
using Microsoft.SqlServer.Server;
using System.Collections;
using System.Data.SqlTypes;

public class SplitIDs
{
    
[SqlFunction(FillRowMethodName = "FillRow")]
    
public static IEnumerable DoSplit(String strIDs)
    {
        
return strIDs.Split(',');
    }

    
public static void FillRow(Object obj, out SqlInt64 id)
    {
        
long value = 0;
        
long.TryParse((string)obj, out value);
        id 
= new SqlInt64(value);
    }
}

2个注意的点
1.     命名空间声明要去掉,我在测试的过程中刚开始有命名空间的声明,总是注册不成功,后来去掉了存储过程的声明,才注册上
2.     方法必须是静态的并且要有SqlFunction特性,表值函数的返回值是IEnurable

第二步:注册程序集到sql server

USE [DB_Name]
GO
if (object_id('SplitIDs'is not null)
drop function splitIds;
GO
IF  EXISTS (SELECT * FROM sys.assemblies asms WHERE asms.name = N'SqlServerUtility')
DROP ASSEMBLY [SqlServerUtility]
go
CREATE ASSEMBLY SqlServerUtility 
FROM 'D:\Program Files\Microsoft SQL Server\90\UserDefinedAssembly\SqlServerUtility.dll' 
WITH PERMISSION_SET = SAFE
GO
CREATE FUNCTION SplitIDs(@ids Nvarchar(max))
RETURNS TABLE (id bigint)
AS 
EXTERNAL NAME SqlServerUtility.SplitIDs.DoSplit
GO
EXEC sp_configure "clr enabled",1   
RECONFIGURE
GO

注册过程中需要注意的是上面注册sql中的最后一块,配置在数据库中启用clr enabled属性。否则即使注册上了函数也不能执行。
注册好了,我们来一个小例子,测试一下函数的执行情况:
select * from splitIDs('1,2,9,1000');
select * from splitIDs('333');
select * from splitIDs(NULL);
执行结果一切正常。
-------------t-sql实现分隔------------
当然这个函数还可以用t-sql直接实现,实现代码如下:

Use DB_Name
GO
if (object_id('splitIds'is not null)
drop function splitIds;
GO
CREATE FUNCTION splitIds(
    
@strIds varchar(max
)
RETURNS @IDs table (id bigint)
AS
BEGIN 
    
declare @i int,@j int,@l int,@v bigint;
    
SET @i = 0;
    
SET @j = 0;
    
SET @l = len(@strIds);
    
while (@j < @l)
    
BEGIN
        
SET @j = charindex(',',@strIds,@i+1);
        
IF @j = 0 set @j = @l+1;
        
SET @v = cast(substring(@strIds,@i+1,@j-@i-1as bigint);
        
INSERT INTO @IDs(id)VALUES(@v)
        
SET @i = @j;
    
END
    
RETURN ;
END
GO

最后测试一下用clr实现和sql直接实现的性能差别;我们声明一个很长的字符串变量,然后让clr的splitIDs和t-sql的splitIDs分别做拆分10次,比较他们的耗时

测试脚本 (在测试脚本中使用的函数名字分别是splitIDs_tsql,splitIDs_clr

t-sql和clr实现的性能比较
执行的结果是:
splitIDs_tsql执行时间10060
splitIDs_clr执行时间266

很明显,用clr写的拆分函数要比tsql的拆分函数快大约50倍。

全文结束
Tag标签: t-sql,clr,function
posted on 2008-04-28 17:08 玉开 阅读(1510) 评论(12)  编辑 收藏 所属分类: 数据库sql server

FeedBack:
#1楼  2008-04-28 18:39 颜斌      

  回复  引用  查看    
#2楼  2008-04-28 21:37 生鱼片      
学习
  回复  引用  查看    
#3楼  2008-04-28 21:42 右手交易      
比较速度的方式有点不公平.

你应该在C#里面也是用WHILE循环加上SUBSTRING的逻辑来拆分.才有可比性.
  回复  引用  查看    
#4楼  2008-04-28 21:48 Jeffrey Zhao      
@右手交易
同样实现一个这种函数的确是CLR快得多。
  回复  引用  查看    
#5楼  2008-04-29 08:28 李战      
俺得到了,谢谢!

关于后面的性能比较
拿T-SQL的短处跟人家长处比,高,实在是高!
  回复  引用  查看    
#6楼 [楼主] 2008-04-29 08:48 玉开      
t-sql确实没有类似c#拆分字符串的函数,所以此处用clr实现了一个。

我们可以把t-sql实现不了的功能用clr实现,比如正则表达式,不过需要正则表达式的操作通常会放在UI层或者逻辑层来做。
  回复  引用  查看    
#7楼  2008-04-29 09:19 airwolf2026      
不错.昨天才知道有这个东东.今天就看到样列,还有相关的性能说明了.
  回复  引用  查看    
#8楼 [楼主] 2008-04-29 09:28 玉开      
@airwolf2026
呵呵,要感谢博客园为我们提供了这种环境。
  回复  引用  查看    
#9楼  2008-04-29 15:02 Henry Liang      
博主钻研精神不错。

只不过如同上面几个人说的,T-SQL实在不是干这个的料。
  回复  引用  查看    
#10楼  2008-05-04 17:37 上午的绝缘杯      
呵呵,学习了!
  回复  引用  查看    
#11楼 [楼主] 2008-05-09 14:16 玉开      
@上午的绝缘杯
呵呵,互相学习
  回复  引用  查看    

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-05-07 09:00 编辑过
 
另存  打印