I have a SQL CLR function like this one:
public partial class UserDefinedFunctions {
[Microsoft.SqlServer.Server.SqlFunction(TableDefinition = "number int", FillRowMethodName = "FillRow")]
public static IEnumerable MyClrFunction(object obj) {
// read obj array input and then
var result = new ArrayList();
result.Add((SqlInt32)1);
result.Add((SqlInt32)2);
result.Add((SqlInt32)3);
return result;
}
public static void FillRow(object obj, out SqlInt32 number) {
number = (SqlInt32)obj;
}
}
I would like to use it this way:
DECLARE @x arrayOfInt
INSERT INTO @x VALUES (10)
INSERT INTO @x VALUES (20)
INSERT INTO @x VALUES (30)
SELECT * FROM dbo.MyClrFunction(@x)
arrayOfInt is:
CREATE TYPE [dbo].[arrayOfInt] AS TABLE(
[item] [int] NOT NULL,
PRIMARY KEY CLUSTERED
(
[item] ASC
) WITH (IGNORE_DUP_KEY = OFF)
)
Problem I have is that arrayOfInt is incompatible with sql_variant. Is possible to write CLR Table-valued function which has an array (table) argument?
SQLCLR does not support Table-Valued Parameters (TVPs):
CREATE PROCEDURE
CREATE FUNCTION
However, you do have several options:
If the array is a simple list of numbers and/or strings you can always send in a delimited list of values (via SqlString / NVARCHAR(MAX)) and use String.Split() to unpack it.
If the array is more complex (i.e. multiple fields), then you can wrap the data in XML and pass that in as SqlXml.
Or, if you have a complex array then you can create a CLR UDT to be whatever structure you like and pass that into the Function. This requires a bit more effort, though.
Also, keep in mind that Table-Valued Parameters are just that, tables (table variables), and not in-memory data structures such as Arrays and Collections. The main benefit and use-case of TVPs are in reducing complexity and increasing performance when sending data to SQL Server from an application. If you are already inside SQL Server such that you are creating a table variable and then wanting to pass it to a CLR stored procedure (or maybe function), then all you need to do is look at the problem in a slightly different way and you can accomplish the same basic thing:
"Context Connection = true;"
) because it can access local temporary objectsSELECT *
on the table name that was passed in, and then read each row viaSqlCommand.ExecuteReader()
andSqlDataReader.Read()