2010-05-24 68 views
0

變量數A CLR UDF我想要一個函數來找到最大的中傳遞的字符串值的列表。創建具有參數

我想要調用它作爲選擇最大(「ABCD」,「EFGH」, 'Zxy','EAD')來自sql server。 它應該返回Zxy。 參數個數是可變的。順便說一句,它與 oracle GREATEST函數非常相似。 所以我寫了一個非常簡單的CLR函數(Vs2008)並試圖部署它。 見下面

public partial class UserDefinedFunctions 
{ 
[Microsoft.SqlServer.Server.SqlFunction] 
public static SqlString Greatest(params SqlString[] p) 
{ 
SqlString max=p[0]; 
foreach (string s in p) 
max = s.CompareTo(max) > 0 ? s : max; 

return max; 

} 
}; 

但是,當我嘗試編譯或部署它,我收到以下錯誤 找不到數據類型的SqlString []。

使用SQL CLR可以滿足我的要求嗎?

+0

爲什麼它必須是CLR函數? – 2010-05-24 06:23:01

回答

1

下面是一個使用表值函數的解決方案:

CREATE FUNCTION fn_Split 
(
    @text VARCHAR(8000), 
    @delimiter VARCHAR(20) = ',' 
) 
    RETURNS @Strings TABLE 
     (
      position INT IDENTITY PRIMARY KEY, 
      value VARCHAR(8000) 
     ) 
AS BEGIN 
    DECLARE @index int 
    SET @index = -1 

    WHILE (LEN(@text) > 0) BEGIN 
     -- Find the first delimiter 
     SET @index = CHARINDEX(@delimiter , @text) 

     -- No delimiter left? 
     -- Insert the remaining @text and break the loop 
     IF (@index = 0) AND (LEN(@text) > 0) BEGIN 
      INSERT INTO @Strings VALUES (LTRIM(RTRIM(@text))) 
      BREAK 
     END 

     -- Found a delimiter 
     -- Insert left of the delimiter and truncate the @text 
     IF (@index > 1) BEGIN 
      INSERT INTO @Strings VALUES (LTRIM(RTRIM(LEFT(@text, @index - 1)))) 
      SET @text = RIGHT(@text, (LEN(@text) - @index)) 
     END 
     -- Delimiter is 1st position = no @text to insert 
     ELSE SET @text = RIGHT(@text, (LEN(@text) - @index)) 
    END 
    RETURN 
END 
GO 

測試:

DECLARE @test varchar(120) 

SET @test = 'Abcd, Efgh, Zxy, EAD' 

SELECT Top(1) value FROM dbo.fn_Split(@test, ',') 
ORDER BY value DESC 

GO 

(從here修改分割功能)

注意:這是幾乎可以肯定不是最快的方法來做到這一點。如果您需要執行數百萬次,另一種解決方案可能更合適。

0

不幸的是,在CLR中聲明UDF是不可能的,只需要你想要的簽名(params SqlString [] p)。 UDF只能具有強類型定義的params列表,而關鍵字「params」目前不受支持(我希望這將在未來也會改變)。

這裏是String.Format UDF的例子。

[SqlFunction(DataAccess = DataAccessKind.None)] 
    public static SqlString clr_StringFormat2(SqlString format, object s1, object s2) 
    { 
     return format.IsNull ? SqlString.Null : new SqlString(string.Format(format.Value, SqlTypeToNetType(s1, s2))); 
    } 

如果你想要更多的參數,你將需要添加另一個UDF。

[SqlFunction(DataAccess = DataAccessKind.None)] 
    public static SqlString clr_StringFormat3(SqlString format, object s1, object s2, object s3) 
    { 
     return format.IsNull ? SqlString.Null : new SqlString(string.Format(format.Value, SqlTypeToNetType(s1, s2, s3))); 
    } 

在.CLR中記住的另一件事是沒有重載方法,所以你的UDF需要有唯一的名稱。

最後,您的UDF無法在.CLR中實現,如果您有/想要無限數量的參數。它只能是固定數量的參數,例如。 4(正如你提到的情況)。

爲什麼在這種情況下使用CLR優於SP的原因是性能要好得多。但我也要指出,這並不意味着你可以通過.CLR獲得更好的性能。在某些情況下,T-SQL/PS的性能會更好。 當然,這裏的一切都取決於您可以在生產環境中部署.CLR的假設。如果我可以將.CLR部署到生產環境並需要數學,字符串操作或類似的東西,我總是使用CLR。

1

不,SQL Server用戶定義函數中不可能有可變數量的參數(即.NET中的params修飾符),無論它們是T-SQL還是SQLCLR。是的,某些內置函數確實允許這樣的事情(例如CHECKSUM(*)),但是這些函數直接構建到SQL Server中,而不是API,例如用戶定義的函數/表值函數。

爲了最好地解決這個問題的目標,在什麼情況下獲得這些價值?他們是多個表或查詢的列嗎?他們是不同的行嗎?這些值是否已經作爲CSV列表連接在一起? T-SQL實際上在自己排列一系列事物方面做得相當不錯。您可能能夠將查詢結構化爲使用OUTER APPLYFROM子句的一部分),可用於在各種情況下執行此操作。例如:

SELECT tab.name AS [TableName], 
     ind.name AS [IndexName], 
     col.name AS [ColumnName], 
     greatest.Item AS [GREATEST()] 
FROM sys.tables tab 
LEFT JOIN (sys.indexes ind 
    INNER JOIN sys.index_columns indcol 
      ON indcol.[object_id] = ind.[object_id] 
      AND indcol.index_id = ind.index_id 
    INNER JOIN sys.columns col 
      ON col.[object_id] = indcol.[object_id] 
      AND col.column_id = indcol.column_id 
     ) 
     ON ind.[object_id] = tab.[object_id] 
OUTER APPLY (SELECT TOP 1 tmp.Name AS [Item] 
      FROM (
        SELECT tab.name UNION ALL SELECT ind.name UNION ALL SELECT col.name 
       ) tmp(Name) 
      ORDER BY tmp.Name ASC 
      ) greatest 

結果是在每行的基礎上的3個行業中有「最偉大」的價值。正如你所看到的,這種方法足夠靈活,可以包含任意數量的列。