SQL2005CLR函数扩展-树的结构
2009-04-28 19:17
489 查看
树结构广泛用在各类分级管理设计中。但他的展现方式也是很让人头疼的事情。比如展开一个靠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函数。
set
nocount
on
'--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
'--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函数。
相关文章推荐
- SQL2005CLR函数扩展-树的结构
- 数据结构4:线性表中的10个扩展函数
- Yii 1.1.17 一、安装、目录结构、视图、控制器、扩展自定义函数
- SQL2005CLR函数扩展-山寨索引
- 函数的扩展---与结构赋值默认值联合使用
- SQL2005CLR函数扩展-数据导出
- SQL2005CLR函数扩展-字符串函数
- SQL2005CLR函数扩展-繁简转换
- SQL2005CLR函数扩展 - 关于山寨索引
- SQL2005CLR函数扩展-字符串函数
- SQL2005CLR函数扩展-深入环比计算的详解
- SQL2005CLR函数扩展-天气服务
- SQL2005CLR函数扩展-字符串函数
- PHP快速定位函数所在扩展模块(顺带说下语言结构和内置函数)
- SQL2005CLR函数扩展-环比计算
- SQL2005CLR函数扩展-数据导出
- SQL2005CLR函数扩展-数据导出的实现详解
- SQL2005CLR函数扩展-繁简转换
- SQL2005CLR函数扩展-天气服务
- SQL2005CLR函数扩展-解析天气服务的实现