2012-02-14 111 views
1

在Oracle它工作得很好......替代在SQL Server 2008 PERCENTILE_CONT

查詢for Oracle是如下

Select distinct channel_id, position_id,datamonth, 
    percentile_cont(.9) within group (order by TRIM_PRE_ELIG_PAY) 
     over (partition by channel_id, position_id, datamonth) as TRIM_PRE_ELIG_PAY_90th_PERC 
from Tablename 

但對於SQL Server中,我發現了一個錯誤。下面是SQL Server 2008中查詢:

Select 
    distinct channel_id, 
    position_id, datamonth, 
    percentile_cont(.9) within group (order by TRIM_PRE_ELIG_PAY) 
    over (partition by channel_id) as TRIM_PRE_ELIG_PAY_90th_PERC 
from table 

ERROR: Select could not be parsed correctly. Output could not be generated.

我知道了,它可以在SQL Server 2012中正常工作,但需要另一種方式在SQL Server 2008

任何人可以幫助... ........

回答

2

有對適用於SQL Server 2005+

不幸的是,SQL Server Engine blog一種解決方法,這是相當長而曲折的:我會離開你個e鏈接,而不是嘗試使其適應您的查詢...

1

您可以創建一個CLR聚合函數來實現它。唯一的缺點是你將不得不重新安排你的查詢。我使用CLR實現了percentile_cont。關於如何創建CLR,請閱讀here。然後,您可以使用此代碼獲得與percentile_cont相同的O/P。它比寫多條語句更容易。

根據您的使用情況,您可以對其進行一定的改進/調整。

using System; 
using System.Data; 
using System.Data.SqlClient; 
using System.Data.SqlTypes; 
using Microsoft.SqlServer.Server; 
using System.Collections.Generic; 


[Serializable] 
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(
    Format.UserDefined, 
    IsInvariantToDuplicates = false, 
    IsInvariantToNulls = false, 
    IsInvariantToOrder = false, 
    MaxByteSize = 8000)] 
public struct Percentile_Cont : IBinarySerialize 
{ 
    //Variables to hold the values; 
    private List<decimal> _list; 
    private decimal _percentile; 

    public void Init() 
    { 
     _list = new List<decimal>(); 
     _percentile = new decimal(); 
    } 

    public void Accumulate(SqlDecimal value,SqlDecimal percentile) 
    { 
     if (!value.IsNull) 
     { 
      _list.Add(value.Value); 
      _percentile = (decimal)percentile; 
     } 
    } 

    /// 

    /// Merge the partially computed aggregate with this aggregate. 
    /// 
    /// The other partial results to be merged 
    public void Merge(Percentile_Cont group) 
    { 
     this._list.AddRange(group._list.ToArray()); 
    } 

    /// 

    /// Called at the end of aggregation, to return the results. 
    /// 
    /// The percentile of all inputted values 
    public SqlDecimal Terminate() 
    { 
     if (_list.Count == 0) 
      return SqlDecimal.Null; 
     _list.Sort(); 

     if (_percentile < 0 || _percentile >= 1) 
      return SqlDecimal.Null; 

     var index = 
      (int) Math.Ceiling 
      (_percentile * _list.Count + 0.5m); 

     if(index > _list.Count) 
     { 
      index = index - 1; 
     } 

     return _list[index-1]; 

    } 


    #region IBinarySerialize Members 

    public void Read(System.IO.BinaryReader binaryReader) 
    { 
     int cnt = binaryReader.ReadInt32(); 
     this._list = new List<decimal>(cnt); 
     this._percentile = new decimal(); 
     for (int i = 0; i < cnt; i++) 
     { 
      this._list.Add(binaryReader.ReadDecimal()); 
     } 
     this._percentile = binaryReader.ReadDecimal(); 
    } 

    public void Write(System.IO.BinaryWriter binaryWriter) 
    { 
     binaryWriter.Write(this._list.Count); 
     foreach (decimal d in this._list) 
     { 
      binaryWriter.Write(d); 
     } 
     binaryWriter.Write(_percentile); 
    } 

    #endregion 
}