2017-08-10 37 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就这个相当简单的特殊情况而言,这还不够公平。但对于更复杂的场景,这种技术相当有用:-)。 –