尽早停止自定义SQLCLR聚合

时间:2017-08-10 18:49:04

标签: sql-server aggregate-functions 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)

现在,没有内置的Aggregate 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时返回0?

1 个答案:

答案 0 :(得分:1)

您无法强制终止,因为无法控制工作流程。当每个组完成处理其集合时,SQL Server将调用Terminate()方法。

但是,由于在组中处理的每一行中都维护了UDA的状态,因此您只需检查runningSum以查看它是否已经0,如果是,则跳过任何计算。这样可以节省一些时间。

Accumulate()方法中,第一步是检查runningSum是否为0,如果为真,则只需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;
}

注意:请勿将value投射到int。所有Sql*类型都具有Value属性,该属性返回预期的本机.NET类型。

最后,在Merge方法中,选中runnningSum以查看它是0还是f,只需return;。然后检查other.runningSum以查看它是否为0,如果为真,则将runnningSum设置为0