您的位置:首页 > 数据库

SQL2005CLR函数扩展-树的结构

2009-04-28 19:17 323 查看
树结构广泛用在各类分级管理设计中。但他的展现方式也是很让人头疼的事情。比如展开一个靠id和parentid建立关系的分级树,SQL2005已经可以用CTE来递归查询。我们看如下测试代码:







set

nocount

on

print

'--SQL2005 CTE

树结构测试

'

declare

@t table

(

id varchar

(

10)

,

pid varchar

(

10),

name

varchar

(

10))



insert

into

@t values

(

'a'

,null,

'000'

)

insert

into

@t values

(

'b'

,

'a'

,

'111'

)

insert

into

@t values

(

'c'

,

'b'

,

'222'

)

insert

into

@t values

(

'd'

,

'b'

,

'333'

)

insert

into

@t values

(

'f'

,

'c'

,

'444'

)

insert

into

@t values

(

'e'

,

'c'

,

'555'

)

;

with

t (

id,

name

,

pid,

path

)

as

(

select

a.

id,

a.

name

,

a.

pid,

cast

(

a.

id as

varchar

(

20))

as

path

from

@t as

a

where

pid is

null



union

all



select

a.

id,

a.

name

,

a.

pid,

cast

(

path

+

'>'

+

a.

id as

varchar

(

20))

from

@t a

join

t as

b

on

a.

pid =

b.

id

)

select

*

from

t



/*

--SQL2005 CTE

树结构测试

id name pid path

----------
---------- ---------- --------------------

a 000 NULL a

b 111 a a>b

c 222 b a>b>c

d 333 b a>b>d

f 444 c a>b>c>f

e 555 c a>b>c>e

*/

set

nocount

off









当你还不知道这个写法的时候可能会有点激动,但是别忘了他是递归,性能并不比以前的循环+表变量

好。我用他展开一个26000条数据,最深5级的权限组分级表时,10分钟后仍然没有查询结束。而我尝试用CLR写扩展函数来实现同样的效果,展开同样的结构,只需要不到2秒

.



下面我们看这个clr的写法,主要思路是提前在内存中build一个链表,可以返回任意节点的路径。当然和之前的几个clr的处理并发方式一样,需要维护一个并发key。







using

System;

using

System.Data;

using

System.Data.SqlClient;

using

System.Data.SqlTypes;

using

Microsoft.SqlServer.Server;



public

partial

class

UserDefinedFunctions

{




private

class

TreeNode


{


public

TreeNode(string

id)


{

this

._id
= id;


}


private

string

_id;




public

TreeNode

parentNode=null

;


public

override

string

ToString()


{

System.Text.StringBuilder

path = new

System.Text.StringBuilder

();

path.Append(this

._id);



TreeNode

parent = this

.parentNode;

while

(parent != null

)

{

path.Insert(0, ">"

);

path.Insert(0, parent._id);

parent = parent.parentNode;

}

return

path.ToString();


}


}




private

class

TreeNodeCollection


{


private

System.Collections.Generic.Dictionary

<string

,
TreeNode

> _nodeList = new

System.Collections.Generic.Dictionary

<string

,
TreeNode

>();




public

TreeNode

AddNode(string

id,TreeNode

node)


{

_nodeList.Add(id, node);

return

node;


}


public

TreeNode

GetNode(string

id)


{

if

(_nodeList.ContainsKey(id))

return

_nodeList[id];


else

return

null

;


}


}




static

System.Collections.Generic.Dictionary

<string

,
TreeNodeCollection

> _tempNodeList = new

System.Collections.Generic.Dictionary

<string

,
TreeNodeCollection

>();




[Microsoft.SqlServer.Server.SqlFunction

]


public

static

SqlBoolean

TreeBuilder(SqlString

key, SqlString

id,SqlString

pid)


{


if

(key.IsNull || id.IsNull) return

true

;






TreeNodeCollection

nodeList =
_tempNodeList[key.Value];




TreeNode

node = nodeList.GetNode(id.Value);




if

(node == null

)


{

node = new

TreeNode

(id.Value);

nodeList.AddNode(id.Value,node);


}






if

(pid.IsNull) return

true

;




TreeNode

pnode =
nodeList.GetNode(pid.Value);




if

(pnode == null

)


{

pnode = new

TreeNode

(pid.Value);

nodeList.AddNode(pid.Value, pnode);


}




node.parentNode = pnode;




return

true

;




}


[Microsoft.SqlServer.Server.SqlFunction

]


public

static

SqlString

GetTreePath(SqlString

key, SqlString

id)


{


if

(key.IsNull || id.IsNull) return

"Null"

;




TreeNodeCollection

nodeList =
_tempNodeList[key.Value];


TreeNode

node =
nodeList.GetNode(id.Value);




if

(node == null

)
return

"No
Node"

;




return

node.ToString();


}


[Microsoft.SqlServer.Server.SqlFunction

]


public

static

SqlBoolean

InitKey(SqlString

key)


{


try


{

_tempNodeList.Add(key.Value, new

TreeNodeCollection

());


return

true

;


}


catch


{

return

false

;


}


}


[Microsoft.SqlServer.Server.SqlFunction

]


public

static

SqlBoolean

DisposeKey(SqlString

key)


{


try


{

_tempNodeList.Remove(key.Value);

return

true

;


}


catch


{

return

false

;


}


}

};



把上面的代码编译为TestTree.dll并复制到服务器的目录中。然后用如下sql语句发布







/*

drop function
dbo.xfn_TreeBuilder

drop function
dbo.xfn_GetTreePath

drop function
dbo.xfn_initTreeKey

drop function
dbo.xfn_disposeTreeKey

drop ASSEMBLY
TestTreeForSQLCLR

*/

CREATE

ASSEMBLY

TestTreeForSQLCLR FROM

'E:/sqlclrdata/TestTree.dll'

WITH

PERMISSION_SET =

UnSAFE;

--

go

CREATE

FUNCTION

dbo.

xfn_TreeBuilder

(



@key nvarchar

(

255),

@id nvarchar

(

255),

@pid nvarchar

(

255)

)



RETURNS

bit

AS

EXTERNAL

NAME

TestTreeForSQLCLR.

[UserDefinedFunctions].

TreeBuilder

go

CREATE

FUNCTION

dbo.

xfn_GetTreePath

(



@key nvarchar

(

255),

@id nvarchar

(

255)

)



RETURNS

nvarchar

(

4000)

AS

EXTERNAL

NAME

TestTreeForSQLCLR.

[UserDefinedFunctions].

GetTreePath

go

CREATE

FUNCTION

dbo.

xfn_initTreeKey

(



@key nvarchar

(

255)

)



RETURNS

bit

AS

EXTERNAL

NAME

TestTreeForSQLCLR.

[UserDefinedFunctions].

InitKey

go

CREATE

FUNCTION

dbo.

xfn_disposeTreeKey

(



@key nvarchar

(

255)

)



RETURNS

bit

AS

EXTERNAL

NAME

TestTreeForSQLCLR.

[UserDefinedFunctions].

DisposeKey













上面包括4个函数,两个负责维护并发key,一个负责建立链表,一个负责返回任意节点的路径。测试sql语句如下









set

nocount

on

print

'--SQL2005 CLR

树结构测试

'

declare

@t table

(

id varchar

(

10)

,

pid varchar

(

10),

name

varchar

(

10))



insert

into

@t values

(

'a'

,null,

'000'

)

insert

into

@t values

(

'b'

,

'a'

,

'111'

)

insert

into

@t values

(

'c'

,

'b'

,

'222'

)

insert

into

@t values

(

'd'

,

'b'

,

'333'

)

insert

into

@t values

(

'f'

,

'c'

,

'444'

)

insert

into

@t values

(

'e'

,

'c'

,

'555'

)



declare

@key varchar

(

40)

,

@b bit

set

@key=

newid

()

select

@b=

dbo.

xfn_inittreekey(

@key)

select

@b=

dbo.

xfn_treebuilder(

@key,

id,

pid)

from

@t

select

*,

cast

(

dbo.

xfn_gettreepath(

@key,

id)

as

varchar

(

20))

as

path

from

@t

select

@b=

dbo.

xfn_disposetreekey(

@key)

go

/*

--SQL2005 CLR

树结构测试

id pid name path

----------
---------- ---------- --------------------

a NULL 000 a

b a 111 a>b

c b 222 a>b>c

d b 333 a>b>d

f c 444 a>b>c>f

e c 555 a>b>c>e

*/

set

nocount

off









这个CLR函数的速度快是因为他牺牲了一部分空间节省了时间,只需要两次全表扫描,时间复杂度应该是O(n)级别的,所以比递归快了很多。目前我还没有发现可以不用提前建立树节点链表集合的方法,所以除了需要调用维护key的两个函数外,还需要调用xfn_treebuilder和

xfn_gettreepath这两个clr函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: