2017-08-10 77 views
1

比方說,我想我有一個表有以下的列和值:停止定製SQLCLR合計年初

| BucketID | Value  | 
|:-----------|------------:| 
| 1   | 3  | 
| 1   | 2  | 
| 1   | 1  | 
| 2   | 0  | 
| 2   | 1  | 
| 2   | 5  | 

讓我們假設我要分區超過BucketId和分區乘以值:

SELECT DISTINCT BucketId, MULT(Value) OVER (PARTITION BY BucketId) 

現在,沒有內建聚集MULT功能,所以我寫我自己的使用SQL CLR:

using System; 
using System.Data.SqlTypes; 
using Microsoft.SqlServer.Server; 

[Serializable] 
[SqlUserDefinedAggregate(Format.Native, Name = "MULT")] 
public struct MULT 
{ 
    private int runningSum; 

    public void Init() 
    { 
     runningSum = 1; 
    } 

    public void Accumulate(SqlInt32 value) 
    { 
     runnningSum *= (int)value; 
    } 

    public void Merge(OverallStatus other) 
    { 
     runnningSum *= other.runningSum; 
    } 

    public SqlInt32 Terminate() 
    { 
     return new SqlInt32(runningSum); 
    } 
} 

我的問題歸結爲,讓我們假設我在AccumulateMerge中達到0,沒有意義繼續。如果是這樣的話,我一打0就該如何返回0?

回答

1

由於無法控制工作流程,因此無法強制終止。當每個組完成處理其集合時,SQL Server將調用Terminate()方法。

但是,由於在組中處理的每行都保持UDA的狀態,因此您可以簡單地檢查runningSum以查看它是否已經爲0,如果是,則跳過任何計算。這可以節省一些時間處理的時間。

Accumulate()方法中,第一步驟是檢查是否runningSum0,並且如果爲真,只需return;。您還應該檢查value是否爲NULL(您目前沒有檢查的東西)。之後,檢查傳入的value以查看它是否爲0,如果屬實,則將runningSum設置爲0return;

public void Accumulate(SqlInt32 value) 
{ 

    if (runningSum == 0 || value.IsNull) 
    { 
    return; 
    } 

    if (value.Value == 0) 
    { 
    runningSum = 0; 
    return; 
    } 

    runningSum *= value.Value; 
} 

注意:不要投valueint。所有Sql*類型都有一個返回預期的本機.NET類型的Value屬性。

最後,在Merge方法,檢查runnningSum,看它是否是0和f真實的,只是return;。然後檢查other.runningSum以查看它是否爲0,如果是,則將runnningSum設置爲0

+0

這將需要仔細測試,看看是否有任何意見。具體而言,插入條件語句將涉及額外的指令和(可能)分支,這些分支可能比僅僅無條件地相乘(取決於JIT編譯器對它的做法)要慢。 –

+0

@JeroenMostert就這個相當簡單的特殊情況而言,這還不夠公平。但對於更復雜的場景,這種技術相當有用:-)。 –