提前停止自定义 SQLCLR 聚合

Stopping a custom SQLCLR aggregate early

假设我想要一个包含以下列和值的 table:

| 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就return0呢?

您无法强制终止,因为无法控制工作流程。 SQL 服务器将在每个组处理完其集合时调用 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 属性,即 returns 预期的本机 .NET 类型。

最后在Merge方法中,检查runnningSum是否为0且f为真,干脆return;。然后查看other.runningSum是否为0,如果为真则将runnningSum设置为0