Cómo devolver varias matrices de dobles como una tabla en la función C # SQLCLR

Oct 09 2020

Lo que tengo es un CLR SqlFunctionque produce 3 matrices de dobles. Me gustaría que esta función devuelva algo apropiado para que FillRowMethodpueda generarlo como una tabla en T-SQL. Funciona bien para 1 matriz, pero tengo problemas para extenderla a varias. En general, no estoy seguro de qué devolver con mi método. Algún código a continuación:

[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
}

EDITAR:

Según los comentarios, este es mi nuevo intento de solución.

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

}

Respuestas

6 SolomonRutzky Oct 09 2020 at 18:50

Si desea devolver las 3 matrices como 3 conjuntos de resultados separados, eso no es posible para una función, ya sea SQLCLR o T-SQL. En su lugar, necesitaría crear un procedimiento almacenado para devolver múltiples conjuntos de resultados.

Si las 3 matrices representan 3 columnas de un único conjunto de resultados de manera que todas tendrán el mismo número de elementos y el valor de índice de una está asociado conceptualmente con el mismo valor de índice de las demás ( array1[x]es decir, está asociado con array2[x]y array3[x], mientras que array1[y]está asociado con array2[y]y array3[y], y así sucesivamente), entonces una matriz simple es el tipo de colección incorrecto. Solo puede devolver una única colección donde cada elemento / elemento de la colección representa una fila en el conjunto de resultados (o al menos suficiente información para construir la fila deseada). Esa colección singular, cuando se devuelve del SqlFunctionmétodo, se itera, llamando a la FillRowMethodpara cada elemento / elemento. Un elemento / elemento se pasa alFillRowMethod que construye la estructura y los valores del conjunto de resultados finales y los devuelve (por lo tanto, tiene la oportunidad de crear y / o traducir valores del elemento original antes de devolverlos).

En este último caso, necesitaría crear una clase similar a:

private class volatility
{
    public double impliedVol;
    public double maturity;
    public double strike;
}

Luego, cree una lista genérica de aquellos en su getStrippedCapletVolatilitiesFromCapVolatilityCurvemétodo, agregue un nuevo elemento a esa colección para cada fila que se devuelva y luego devuelva esa lista / colección. Se FillRowMethodle llamará con el primer parámetro (as object) de tipo volatility. Ahí es donde poblará los outparámetros a partir de esas propiedades de volatility. Por ejemplo:

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

Ahora, podría ser posible manejar esto como una matriz bidimensional (es decir double[][]) que regresa de la principal SqlFunction, pero luego el FillRowmétodo se enviaría double[]ya que la primera dimensión se divide en llamadas individuales al FillRowmétodo. Nunca probé este enfoque específico, pero debería funcionar de la siguiente manera:

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

ADEMÁS:

Se me acaba de ocurrir que incluso podría renunciar a la lista / colección genérica y transmitir el contenido de la double[][]matriz al conjunto de resultados, un elemento / fila a la vez. Adelante, prueba esto:

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

Existen algunas restricciones al usar yield return, pero si su proceso permite esta construcción, esto no solo será más rápido, sino que también ocupará menos memoria. Estos beneficios se deben a que este código omite el paso de copiar los resultados del getCapletCurveForGivenStrike()método en una colección separada (es decir, la Lista genérica) solo para devolverlo a T-SQL (en cuyo caso debe esperar mientras copia la colección y consume más memoria).


En una nota relacionada: use los Sql*tipos para los parámetros de entrada en lugar de los tipos estándar de .NET. Es decir, use en SqlStringlugar de stringy en SqlDoublelugar de double. A continuación, puede obtener fácilmente el tipo nativo .NET de aquellos a través de la Valuepropiedad que tienen todos los Sql*tipos (por ejemplo, SqlString.Valuedevuelve a string, etc.).

Para obtener más información sobre cómo trabajar con SQLCLR en general, visite: SQLCLR Info