日期:2014-05-18  浏览次数:20593 次

【交流】SQL2005CLR函数扩展-环比计算
blog原文,希望大家支持我的blog
http://blog.csdn.net/jinjazz/archive/2009/04/28/4132162.aspx

---------

环比就是本月和上月的差值所占上月值的比例。在复杂的olap计算中我们经常会用到同比环比等概念,要求的上个维度的某个字段的实现语句非常简练,比如ssas的mdx语句类似[维度].CurrentMember.Prevmember就可以了。此类问题还可以延伸到类似进销存的批次计算中,这也要关注其他历史记录来决定当前某条记录的状态。

  sql语句无法简单实现mdx语句的类似功能,必须得用交叉表关联来对比。这里我们用CLR函数来实现mdx语句的类似语法。在select的时候把得到过的做个缓存就可以了。效率应该可以提高不少。

  clr的代码如下,编译为TestFun.dll,复制到sql服务器的文件目录下
C# code
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public partial class UserDefinedFunctions
{

    //保存当前组当前值
    private static System.Collections.Generic.Dictionary<string, SqlString> _listValue = new System.Collections.Generic.Dictionary<string, SqlString>();
    //保存当前组
    private static System.Collections.Generic.Dictionary<string, string> _listGroup  = new System.Collections.Generic.Dictionary<string, string>();

    /// <summary>
    /// 获取当前组上条记录数值
    /// </summary>
    /// <param name="key">并发键</param>
    /// <param name="currentGroup">当前组</param>
    /// <param name="currentValue">当前组当前值</param>
    /// <returns></returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlString GetPrevMemberValue(SqlString key,SqlString currentGroup,SqlString currentValue)
    {
        if (key.IsNull || currentGroup.IsNull) return SqlString.Null;

       
        try
        {
            SqlString prevMemberValue = _listValue[key.Value];

            //组变更
            if (_listGroup[key.Value] != currentGroup.Value)
            {
                prevMemberValue = SqlString.Null;
                _listGroup[key.Value] = currentGroup.Value;
            }
            //值变更
            _listValue[key.Value] = currentValue;

            return prevMemberValue;
        }
        catch
        {
            return SqlString.Null;
        }
    }
    /// <summary>
    /// 初始化并发键
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlBoolean InitKey(SqlString key)
    {
        try
        {
            _listValue.Add(key.Value, SqlString.Null);
            _listGroup.Add(key.Value, string.Empty);
            return true;
        }
        catch
        {
            return false;
        }
    }
    /// <summary>
    /// 释放并发键
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlBoolean DisposeKey(SqlString key)
    {
        try
        {
            _listValue.Remove(key.Value);
            _listGroup.Remove(key.Value);
            return true;
        }
        catch
        {
            return false;
        }
    }
};



部署和生成自定义函数,其中考虑到并发,我们还是需要一个并发键来表达当前查询
SQL code
CREATE ASSEMBLY TestForSQLCLR FROM 'E:\sqlclrdata\TestFun.dll' WITH PERMISSION_SET = UnSAFE;
--
go
--获取上个维度
CREATE FUNCTION dbo.xfn_GetPrevMemberValue  
(  
    @key nvarchar(255),
    @initByDim nvarchar(255),
    @currentValue nvarchar(255)
)    
RETURNS nvarchar(255)
AS EXTERNAL NAME TestForSQLCLR.[UserDefinedFunctions].GetPrevMemberValue
go

--初始化并发键
CREATE FUNCTION dbo.xfn_initKey
(  
    @key nvarchar(255)
)    
RETURNS bit
AS EXTERNAL NAME TestForSQLCLR.[UserDefinedFunctions].InitKey
go
--清理并发键
CREATE FUNCTION dbo.xfn_disposeKey  
(  
    @key nvarchar(255)
)    
RETURNS bit
AS EXTERNAL NAME TestForSQLCLR.[UserDefinedFu