C# 如何为运行total函数的SQL CLR创建重置?

C# 如何为运行total函数的SQL CLR创建重置?,c#,sql,.net,sql-server,sql-server-2008,C#,Sql,.net,Sql Server,Sql Server 2008,我正在尝试创建一个SQL CLR函数,该函数执行与此处所述类似的运行总计: 但是,此函数仅对一列中的所有值进行运行合计。我试图做的是重置运行总数,以便在field1(SQL varchar)和field2(SQL numeric)之间发生转换时,它将变为0。然而,我似乎无法找出执行此操作所需的代码。我曾尝试使用CallContext存储这两个字段,但始终遇到空引用。我认为转换检测的工作原理与运行的total计算类似,但事实并非如此。我已经绞尽脑汁好几个小时了,运气不好 这是我使用此函数所针对的表

我正在尝试创建一个SQL CLR函数,该函数执行与此处所述类似的运行总计:

但是,此函数仅对一列中的所有值进行运行合计。我试图做的是重置运行总数,以便在field1(SQL varchar)和field2(SQL numeric)之间发生转换时,它将变为0。然而,我似乎无法找出执行此操作所需的代码。我曾尝试使用CallContext存储这两个字段,但始终遇到空引用。我认为转换检测的工作原理与运行的total计算类似,但事实并非如此。我已经绞尽脑汁好几个小时了,运气不好

这是我使用此函数所针对的表,主键是
REF\u NO

CREATE TABLE[dbo]。[USER\u TB\u TIME\u TICKETS](
[REF_NO][dbo]。[T_DOC_NO]不为空,
[REF_DAT][dbo]。[T_DAT_SMALL]不为空,
[ACT_DAT][dbo].[T_DAT_SMALL]不为空,
[USR\u ID][dbo]。[T\u USR\u ID]不为空,
[CUST_NO][dbo]。[T_CUST_NO]不为空,
[JOB_NO][dbo]。[USER_TB_T_JOB_NO]不为空,
[CODE][dbo]。[USER\u TB\u T\u SERV\u COD]不为空,
[HRS_WORKED][dbo]。[USER_TB_T_HOURS]不为空,
[DESCR][varchar](200)不为空,
[里程][dbo]。[USER\u TB\u T\u MILES]空,
[NOTES][dbo].[USER\u TB\u T\u NOTE]空,
[BILL_FLG][varchar](1)空,
[BILL_HRS][decimal](6,2)空,
[超额工时][十进制](6,2)空,
)

下面是SQL标量CLR函数包装器:

CREATE FUNCTION[dbo]。[fn\u RunningTotalDecimal\u 15\u 2\u ResetStringNumeric](
@val[十进制](15,2),
@id[tinyint],
@罗维诺[int],
@空值[十进制](15,2),
@字段1[nvarchar](15),
@字段2[数字](18,0))
返回[decimal](15,2),并以调用方身份执行
作为
外部名称[SqlClrRunningTotals]。[RDSAPI.SqlClrRunningTotals.RunningTotals]。[RunningTotalDecimalResetStringNumeric]

这是C代码:

编辑

这里有一张图片让它更清晰一点。(感兴趣的栏目被盯上了。我会放一张图片,但我没有足够的代表)

客户编号、工作编号、用户ID、参考编号、参考日期、行动日期、工作小时数、报价小时数、账单金额、账单金额、超额小时数、运行总计 《反洗钱法》第1版,第1523642015-06-182015-06-16,0.25,12.00页,Y,0.25,0.00,9.50页 美国航空航天局,美国航空航天局,1523672015-06-182015-06-18,0.25,12.00,Y,0.25,0.00,9.75 《反洗钱法》第1版,第1523722015-06-182015-06-18,1.50,12.00页,第1.50,0.00,11.25页 美国航空航天局,美国航空航天局,1525692015-06-222015-06-22,0.50,12.00,Y,0.50,0.00,11.75 美国航空航天局,美国航空航天局,1527352015-06-252015-06-25,0.50,12.00,Y,0.25,0.25,12.25 **ATA**,**1**,AMA,1534722015-07-142015-07-13,0.25,12.00,N,0.00,0.25,**12.50** **ATA**,**2**,SCP,1520972015-06-122015-06-10,0.50,3.00,Y,0.50,0.00,**13.00** ATA,2,CTK,1519232015-06-112015-06-11,0.75,3.00,Y,0.75,0.00,13.75 ATA,2,CTK,1519982015-06-122015-06-12,0.75,3.00,Y,0.75,0.00,14.50 我试图做的是更改C代码,这样我就可以检测到客户号或作业号之间的更改(如果客户号相同),并在转换时将running total列重置为0。基本上,我想对每个客户的每个作业编号进行运行总计。我知道这是一个分组函数,但我使用CLR函数,因为在SQL中高效计算运行总计是很困难的,我将对一个每天都在增长的140000多条记录的表执行此操作。因为我使用的是SQL Server 2008 R2,所以2012年我没有窗口行功能,所以我可以将读取数据大幅降低到足以对该数据运行报告的程度的唯一方法是使用CLR函数。本文介绍了性能测试:

另一次编辑


我也愿意接受其他实现的建议。我目前确实有一个游标实现,但同样,它相当慢。我已经记录了它仍在执行超过30秒

您不需要重新编写CLR函数来解决此问题,只需正确地对数据进行分区即可


dbo.fn\u运行totalbigint(BILL\u HRS,JOB\u NO,ROW\u NUMBER()(按客户划分,JOB\u NO ORDER BY REF\u DAT),null)

您无需重新编写CLR函数来解决此问题,只需正确地划分数据即可


dbo.fn\u运行TotalBigint(账单小时数、作业编号、行编号()(按客户编号划分,作业编号按参考数据划分),null)

“字段1和字段2之间的转换”。我不知道你这是什么意思。你能提供一些样本数据吗(最好是你预期的跑步总量的附加列)?是的,当然,我今晚或明天会谈到。听起来你好像在尝试某种
分组行为,但我不完全确定。举个例子会很有帮助,“field1和field2之间的转换”。我不知道你这是什么意思。你能提供一些样本数据吗(最好是你预期的跑步总量的附加列)?是的,当然,我今晚或明天会谈到。听起来你好像在尝试某种
分组行为,但我不完全确定。举个例子会很有帮助。
/// <summary>
    /// Storage Structure for holding actual Total and row number for security check.
    /// </summary>
    /// <typeparam name="T">Totals Data Type</typeparam>
    private struct RtStorage<T> where T : struct
    {
        public T Total;
        public int RowNo;
    }

    private struct StringFieldStorage<T> where T : struct
    {
        public T stringField;    
    }

    private struct NumericFieldStorage<T> where T : struct
    {
        public T numericField;
    }
 /// <summary>
    /// Calculates a running totals on Decimal data type based on transistion between a string and numeric field.
    /// </summary>
    /// <param name="val">Value of current row</param>
    /// <param name="id">ID of the function in single query</param>
    /// <param name="rowNo">Specifies expecter rowNo. It is for security check to ensure correctness of running totals</param>
    /// <param name="nullValue">Value to be used for NULL values</param>
    /// <param name="field1">String field</param>
    /// <param name="field2">Numeric field</param>
    /// <returns>SqlDecimal representing running total</returns>
    [SqlFunction(IsDeterministic = true)]
    public static SqlDecimal RunningTotalDecimalResetStringNumeric(SqlDecimal val, SqlByte id, int rowNo, SqlDecimal nullValue, SqlString field1, SqlDecimal field2)
    {
        string dataName = string.Format("MultiSqlRt_{0}", id.IsNull ? 0 : id.Value);
        string field1Name = string.Format("MultiSqlField1_{0}", id.IsNull ? 0 : id.Value);
        string field2Name = string.Format("MultiSqlField2_{0}", id.IsNull ? 0 : id.Value);

        object lastSum = CallContext.GetData(dataName);
        object field1Value = CallContext.GetData(field1Name);
        object field2Value = CallContext.GetData(field2Name);

        var storage = lastSum != null ? (RtStorage<SqlDecimal>)lastSum : new RtStorage<SqlDecimal>();
        storage.RowNo++;

        var stringFieldStorage = field1Value != null ? (StringFieldStorage<SqlString>)field1Value : new StringFieldStorage<SqlString>();
        var numericFieldStorage = field2Value != null ? (NumericFieldStorage<SqlDecimal>)field2Value : new NumericFieldStorage<SqlDecimal>();

        if (storage.RowNo != rowNo)
            throw new System.InvalidOperationException(string.Format("Rows were processed out of expected order. Expected RowNo: {0}, received RowNo: {1}", storage.RowNo, rowNo));

        if (stringFieldStorage.stringField != field1 || (stringFieldStorage.stringField == field1 && numericFieldStorage.numericField != field2))
        {
            storage.Total = new SqlDecimal(0);
            stringFieldStorage.stringField = field1;
            numericFieldStorage.numericField = field2;
        }

        if (!val.IsNull)
            storage.Total = storage.Total.IsNull ? val : storage.Total + val;
        else
            storage.Total = storage.Total.IsNull ? nullValue : (nullValue.IsNull ? storage.Total : storage.Total + nullValue);

        CallContext.SetData(dataName, storage);
        CallContext.SetData(field1Name, stringFieldStorage);
        CallContext.SetData(field2Name, numericFieldStorage);

        return storage.Total;
    }
CUST_NO,JOB_NO,USR_ID,REF_NO,REF_DAT,ACT_DAT,HRS_WORKED,HOURS_QUOTED,BILL_FLG,BILL_HRS,EXCESS_HRS,running_total ATA,1,AML,152364,2015-06-18,2015-06-16,0.25,12.00,Y,0.25,0.00,9.50 ATA,1,AMA,152367,2015-06-18,2015-06-18,0.25,12.00,Y,0.25,0.00,9.75 ATA,1,AML,152372,2015-06-18,2015-06-18,1.50,12.00,Y,1.50,0.00,11.25 ATA,1,AMA,152569,2015-06-22,2015-06-22,0.50,12.00,Y,0.50,0.00,11.75 ATA,1,AMA,152735,2015-06-25,2015-06-25,0.50,12.00,Y,0.25,0.25,12.25 **ATA**,**1**,AMA,153472,2015-07-14,2015-07-13,0.25,12.00,N,0.00,0.25,**12.50** **ATA**,**2**,SCP,152097,2015-06-12,2015-06-10,0.50,3.00,Y,0.50,0.00,**13.00** ATA,2,CTK,151923,2015-06-11,2015-06-11,0.75,3.00,Y,0.75,0.00,13.75 ATA,2,CTK,151998,2015-06-12,2015-06-12,0.75,3.00,Y,0.75,0.00,14.50