VS2010写SQLSERVER自定义聚合函数

一个客服答疑的模块,主贴(qna_ask)、回复帖(qna_replay )分别是两个表,需要获取每个主贴最新的回复贴。


这里不考虑效率,只是说一条语句的实现问题。


先看MySql:

select * from

(

SELECT

qna_ask.qa_id,

...

qna_replay.qr_time,

...

FROM

qna_ask

INNER JOIN qna_replay ON qna_replay.qa_id = qna_ask.qa_id

ORDER BY qa_id, qr_time desc) as v

group by v.qa_id


Mysql 在Group By存在的情况下,SELECT 的内容,不仅仅是Group By的字段和聚合函数,可以是任何内容,只是有些返回是没有意义的。


SqlServer 在Group By存在的情况下,SELECT 的内容,只能是Group By的字段和聚合函数。Sqlserver聚合函数只有那可怜的几个,于是,决定写一个返回第一个字段内容的函数,FirstVal,据说SqlServer 2012会有个这个函数。

select sex,dbo.FirstVal(name) as n, dbo.FirstVal(weight) as w from

(

SELECT top(99.9999999999999) percent * from dbo.[user]

order by sex , weight desc

)v

group by v.sex


需要写一个Sqlserver 的 CLR 自定义聚合函数。很简单的,VS2010 有wizard。


using System;

using System.Data;

using Microsoft.SqlServer.Server;

using System.Data.SqlTypes;

using System.IO;

using System.Text;



[Serializable]

[SqlUserDefinedAggregate(

    Format.UserDefined, //use custom serialization to serialize the intermediate result

    IsInvariantToNulls = true, //optimizer property

    IsInvariantToDuplicates = false, //optimizer property

    IsInvariantToOrder = false, //optimizer property

    MaxByteSize = 8000) //maximum size in bytes of persisted value

]

public class FirstVal : IBinarySerialize

{

    /// <summary>

    /// The variable that holds the intermediate result of the concatenation

    /// </summary>

    private StringBuilder intermediateResult;



    private Boolean isAssignment;


    /// <summary>

    /// Initialize the internal data structures

    /// </summary>

    public void Init()

    {

        this.intermediateResult = new StringBuilder();

        isAssignment = false;

    }

    /// <summary>

    /// Accumulate the next value, not if the value is null

    /// </summary>

    /// <param></param>

    public void Accumulate(SqlString value)

    {

        if (!isAssignment)

        {

            isAssignment = true;

            intermediateResult.Append(value.Value);

        }

        return;

    }


    /// <summary>

    /// Merge the partially computed aggregate with this aggregate.

    /// </summary>

    /// <param></param>

    public void Merge(FirstVal other)

    {

        this.intermediateResult.Append(other.intermediateResult);

    }


    /// <summary>

    /// Called at the end of aggregation, to return the results of the aggregation.

    /// </summary>

    /// <returns></returns>

    public SqlString Terminate()

    {

        return new SqlString(intermediateResult.ToString());

    }


    public void Read(BinaryReader r)

    {

        intermediateResult = new StringBuilder(r.ReadString());

    }


    public void Write(BinaryWriter w)

    {

        w.Write(this.intermediateResult.ToString());

    }

}


要调试,修改一下儿Test.sql的内容,比如就是上一段SQL就可以进入调试。


SQL Server 查询子句不支持order by, 必须要加上 SELECT TOP number|percent。

SELECT top(99.9999999999999) percent * from dbo.[user]

order by sex , weight desc


发现问题了,SELECT top(100) percent ,在CLR的 void Accumulate(SqlString value)中接受到的数据是不经过排序的。数字随便写都排序,小于100%也可以,于是诞生了上面SELECT top(99.9999999999999),小数点后面13个,再多,就又不排序了。或者干脆写个大整数比如几千亿也排序。


就到这里吧。。。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/11261.html