zoukankan      html  css  js  c++  java
  • SQL2005CLR函数扩展树的结构

          树结构广泛用在各类分级管理设计中。但他的展现方式也是很让人头疼的事情。比如展开一个靠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

          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函数。

  • 相关阅读:
    洛谷P2221 [HAOI2012]高速公路(线段树+概率期望)
    洛谷P2254 [NOI2005]瑰丽华尔兹(单调队列)
    洛谷P2607 [ZJOI2008]骑士(基环树)
    洛谷P1505 [国家集训队]旅游(树剖+线段树)
    洛谷P3250 [HNOI2016]网络(整体二分+树状数组+树剖)
    洛谷P3833 [SHOI2012]魔法树(树链剖分)
    洛谷P4216 [SCOI2015]情报传递(树剖+主席树)
    Ansible 利用playbook批量部署Nginx
    Ansible实现zabbix服务器agent端批量部署
    时间序列模型:ARIMA
  • 原文地址:https://www.cnblogs.com/cl1024cl/p/6204835.html
Copyright © 2011-2022 走看看