Sql-Server

如何在 C# SQLCLR 函式中將多個雙精度數組作為表返回

  • October 10, 2020

我所擁有的是一個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
}

編輯:

根據回饋,這是我嘗試的新解決方案。

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

}

如果您想將 3 個數組作為 3 個單獨的結果集返回,那麼無論是 SQLCLR 還是 T-SQL,這對於函式都是不可能的。您需要創建一個儲存過程來返回多個結果集。

如果 3 個數組表示單個結果集的 3 列,則它們都將具有相同數量的項目*,並且*其中一個的索引值在概念上與其他的相同索引值相關聯(即array1[x]array2[x]and相關聯array3[x],而array1[y]與和array2[y]array3[y]),那麼簡單數組是錯誤的集合類型。您只能返回一個集合,其中集合中的每個項目/元素代表結果集中的一行(或至少有足夠的資訊來構造所需的行)。當從SqlFunction方法返回時,該單一集合被迭代,呼叫FillRowMethod為每個項目/元素呼叫。一項/元素被傳遞到FillRowMethod它構造最終的結果集結構和值並將它們傳回(因此您有機會在將它們傳回之前從原始項目創建和/或轉換值)。

在後一種情況下,您需要創建一個類似於以下內容的類:

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

然後,在您的方法中創建一個通用列表getStrippedCapletVolatilitiesFromCapVolatilityCurve,將一個新項目添加到該集合以使每一行返回,然後返回該列表/集合。FillRowMethod將使用object類型為 的第一個參數(as)呼叫您volatility。這就是您out將從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);
}

double[][]現在,可能可以將其作為從 main 返回的二維數組(即 )來處理SqlFunction,但是隨後該FillRow方法將被發送 a 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]);
}

還:

我突然想到,您甚至可以放棄通用列表/集合併將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;
}

使用時有一些限制yield return,但是如果您的程序允許這種構造,那麼這不僅會更快,而且佔用的記憶體也更少。這些好處是由於此程式碼跳過了將getCapletCurveForGivenStrike()方法的結果復製到單獨的集合(即通用列表)的步驟,只是為了將其返回到 T-SQL(在這種情況下,您需要等待它複製集合併使用更多記憶體)。


在相關說明中:使用Sql*輸入參數的類型而不是標準的 .NET 類型。意思是,使用SqlString代替stringSqlDouble代替doubleValue然後,您可以通過所有類型都具有的屬性輕鬆地從那些中獲取 .NET 本機類型Sql*(例如,SqlString.Value傳回 astring等)。

有關一般使用 SQLCLR 的更多資訊,請訪問:SQLCLR 資訊

引用自:https://dba.stackexchange.com/questions/276791