如何在 C# SQLCLR 函数中将多个双精度数组作为表返回

Osc*_*car 6 sql-server c# sql-clr set-returning-functions

我拥有的是一个SqlFunction生成 3 个双精度数组的CLR 。我希望这个函数返回一些适当的东西,以便FillRowMethod可以将它作为 T-SQL 中的表输出给我。它适用于 1 个数组,但我无法将其扩展到多个数组。我主要不确定从我的方法返回什么。下面的一些代码:

[SqlFunction(DataAccess = DataAccessKind.Read, FillRowMethodName = "FillRow",
    TableDefinition = "impliedVol float, maturity float, strike float")]
public static IEnumerable getStrippedCapletVolatilitiesFromCapVolatilityCurve(
     string uploadDate, double strike, double yearsForward, double intervalDuration,
     string curve, string surface)
    
//Create 3 arrays of doubles
    double[] array1;
    double[] array2;
    double[] array3;

    return [???];
}
    

public static void FillRow(object obj,
     out SqlDouble impliedVol, out SqlDouble maturity, out SqlDouble strike)
{
   //impliedVol = (double)obj;  //This is what I do if only returning one array
}
Run Code Online (Sandbox Code Playgroud)

编辑:

根据反馈,这是我新尝试的解决方案。

public static IEnumerable getStrippedCapletVolatilitiesFromCapVolatilityCurve(string uploadDate, double strike, double yearsForward, double intervalDuration, string curve, string surface)
    {
            //omitted code above this line.
        CapletStipping thisCapletStripping = new CapletStipping(maturities, forwardRates, discountingRates, intervalDuration);
        double[][] theseStrippedCapletVols = thisCapletStripping.getCapletCurveForGivenStrike(flatVols, strike);

         List<capletVolatilityNode> capletVolatilitiesList = new List<capletVolatilityNode>(theseStrippedCapletVols[0].Length);

        for (int i = 0; i < theseStrippedCapletVols[0].Length; i += 1)
        {
            capletVolatilityNode thisCapletVolatilityNode = new capletVolatilityNode(theseStrippedCapletVols[0][i], theseStrippedCapletVols[1][i], theseStrippedCapletVols[2][i]);
            capletVolatilitiesList[i] = thisCapletVolatilityNode;
        }

        return capletVolatilitiesList; // theseStrippedCapletVols;
    }

    public class capletVolatilityNode
    {
        public double impliedVol;
        public double maturity;
        public double strike;
        public capletVolatilityNode(double impliedVol_, double maturity_, double strike_)
        {
            impliedVol = impliedVol_; 
            maturity = maturity_; 
            strike = strike_;
        }
    }

public static void FillRow(Object obj, out SqlDouble impliedVol, out SqlDouble maturity, out SqlDouble strike)
{

    capletVolatilityNode row = (capletVolatilityNode)obj;

    impliedVol = Convert.ToDouble(row.impliedVol);
    maturity = Convert.ToDouble(row.maturity);
    strike = Convert.ToDouble(row.strike);

}
Run Code Online (Sandbox Code Playgroud)

Sol*_*zky 6

如果您想将 3 个数组作为 3 个单独的结果集返回,这对于函数来说是不可能的,无论是 SQLCLR 还是 T-SQL。您需要创建一个存储过程才能返回多个结果集。

如果这 3 个数组表示单个结果集的 3 列,这样它们都将具有相同数量的项目,并且一个的索引值在概念上与其他数组的相同索引值array1[x]相关联(即与array2[x]和相关联array3[x],而array1[y]与用array2[y]array3[y],等等),则一个简单的数组是错误类型的集合。您只能返回一个集合,其中集合中的每个项目/元素代表结果集中的一行(或至少有足够的信息来构造所需的行)。当从该SqlFunction方法返回时,该单一集合被迭代,FillRowMethod为每个项目/元素调用。一个项目/元素被传递到FillRowMethod 它构造最终结果集结构和值并将它们传回(因此您有机会在传回之前从原始项目创建和/或转换值)。

在后一种情况下,您需要创建一个类似于以下内容的类:

private class volatility
{
    public double impliedVol;
    public double maturity;
    public double strike;
}
Run Code Online (Sandbox Code Playgroud)

然后,在您的getStrippedCapletVolatilitiesFromCapVolatilityCurve方法中创建一个通用列表,为要返回的每一行向该集合添加一个新项目,然后返回该列表/集合。您FillRowMethod将被调用,第一个参数 (as object) 的类型为volatility。这就是你将填充out从这些属性PARAMS volatility。例如:

private static void FillRow(object obj,
     out SqlDouble impliedVol, out SqlDouble maturity, out SqlDouble strike)
{
    volatility row = (volatility)obj;

    impliedVol = new SqlDouble(row.impliedVol);
    maturity = new SqlDouble(row.maturity);
    strike = new SqlDouble(row.strike);
}
Run Code Online (Sandbox Code Playgroud)

现在,可以将其作为double[][]从 main 返回的二维数组(即)来处理SqlFunction,但随后该FillRow方法将被发送,double[]因为第一个维度被分解为对该FillRow方法的单独调用。我从未尝试过这种特定方法,但它应该如下工作:

private static void FillRow(object obj,
     out SqlDouble impliedVol, out SqlDouble maturity, out SqlDouble strike)
{
    double[] row = (double[])obj;

    impliedVol = new SqlDouble(row[0]);
    maturity = new SqlDouble(row[1]);
    strike = new SqlDouble(row[2]);
}
Run Code Online (Sandbox Code Playgroud)

还:

double[][]突然想到您甚至可以放弃通用列表/集合并将数组的内容流式传输到结果集,一次一个项目/一行。继续尝试这个:

public static IEnumerable getStrippedCapletVolatilitiesFromCapVolatilityCurve(...)
{
    //omitted code above this line.
    CapletStipping thisCapletStripping =
       new CapletStipping(maturities, forwardRates, discountingRates, intervalDuration);
    double[][] theseStrippedCapletVols =
       thisCapletStripping.getCapletCurveForGivenStrike(flatVols, strike);

    // THIS PART IS DIFFERENT -- begin
    capletVolatilityNode thisCapletVolatilityNode = new capletVolatilityNode();

    for (int i = 0; i < theseStrippedCapletVols[0].Length; i += 1)
    {
        thisCapletVolatilityNode.impliedVol = theseStrippedCapletVols[0][i];
        thisCapletVolatilityNode.maturity = theseStrippedCapletVols[1][i];
        thisCapletVolatilityNode.strike = theseStrippedCapletVols[2][i];

        yield return thisCapletVolatilityNode; // return rows individually
    }

    return; // cannot return anything when using "yield return"
    // THIS PART IS DIFFERENT -- end
}

private class capletVolatilityNode
{
    public double impliedVol;
    public double maturity;
    public double strike;
}
Run Code Online (Sandbox Code Playgroud)

使用时有一些限制yield return,但如果您的进程允许这种构造,那么这不仅会更快,而且占用的内存也更少。这些好处是由于这段代码跳过了将getCapletCurveForGivenStrike()方法的结果复制到单独的集合(即泛型列表)的步骤,只是为了将其返回给 T-SQL(在这种情况下,您需要等待它复制集合并消耗更多内存)。


相关说明:使用Sql*输入参数的类型而不是标准的 .NET 类型。意思是,使用SqlString代替stringSqlDouble代替double。然后,您可以通过Value所有Sql*类型具有的属性(例如SqlString.Value传回 astring等)轻松地从这些类型中获取 .NET 本机类型。

有关使用 SQLCLR 的更多信息,请访问:SQLCLR 信息