提前停止自定义 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);
}
}
我的问题归结为,假设我在 Accumulate
或 Merge
中输入了 0,继续下去没有意义。那样的话,我怎么一打0就return0呢?
您无法强制终止,因为无法控制工作流程。 SQL 服务器将在每个组处理完其集合时调用 Terminate()
方法。
但是,由于 UDA 的状态在组中处理的每一行中都保持不变,您可以简单地检查 runningSum
以查看它是否已经 0
,如果是,则跳过任何计算。这将节省一些处理时间。
在Accumulate()
方法中,第一步是检查runningSum
是否为0
,如果为真,则简单地return;
。您还应该检查 value
是否为 NULL
(您当前未检查的内容)。之后检查传入的value
是否为0
,如果为真则将runningSum
设置为0
和return;
.
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
。
假设我想要一个包含以下列和值的 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);
}
}
我的问题归结为,假设我在 Accumulate
或 Merge
中输入了 0,继续下去没有意义。那样的话,我怎么一打0就return0呢?
您无法强制终止,因为无法控制工作流程。 SQL 服务器将在每个组处理完其集合时调用 Terminate()
方法。
但是,由于 UDA 的状态在组中处理的每一行中都保持不变,您可以简单地检查 runningSum
以查看它是否已经 0
,如果是,则跳过任何计算。这将节省一些处理时间。
在Accumulate()
方法中,第一步是检查runningSum
是否为0
,如果为真,则简单地return;
。您还应该检查 value
是否为 NULL
(您当前未检查的内容)。之后检查传入的value
是否为0
,如果为真则将runningSum
设置为0
和return;
.
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
。