Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Ранняя остановка пользовательского агрегата SQLCLR

Автор вопроса: MattОтветов: 1

Допустим, я хочу, чтобы у меня была таблица со следующими столбцами и значениями:

| 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);
    }
}

Мой вопрос сводится к следующему: предположим, я набрал 0 в Accumulate или Merge, нет смысла продолжать. Если это так, как я могу вернуть 0, как только я нажму 0?

sql-serveraggregate-functionssqlclr

Ответы

Ответ 1 · рейтинг 1 · Solomon Rutzky

Вы не можете принудительно завершить работу, поскольку нет возможности контролировать рабочий процесс. SQL Server вызовет метод 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, которое возвращает ожидаемый собственный тип .NET.

Наконец, в методе Merge проверьте runnningSum, чтобы убедиться, что это 0, и если оно истинно, просто return;. Затем проверьте other.runningSum, чтобы убедиться, что это 0, и если это правда, то установите runnningSum на 0.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы