Reputation: 4468
We have a third-party DLL that can operate on a DataTable of source information and generate some useful values, and we're trying to hook it up through SQLCLR to be callable as a table-valued UDF in SQL Server 2008.
Taking the concept here one step further, I would like to program a CLR Table-Valued Function that operates on a table of source data from the DB.
I'm pretty sure I understand what needs to happen on the T-SQL side of things; but, what should the method signature look like in the .NET (C#) code? What would be the parameter datatype for "table data from SQL Server?"
e.g.
/* Setup */
CREATE TYPE InTableType
AS TABLE (LocationName VARCHAR(50), Lat FLOAT, Lon FLOAT)
GO
CREATE TYPE OutTableType
AS TABLE (LocationName VARCHAR(50), NeighborName VARCHAR(50), Distance FLOAT)
GO
CREATE ASSEMBLY myCLRAssembly
FROM 'D:\assemblies\myCLR_UDFs.dll'
WITH PERMISSION_SET = EXTERNAL_ACCESS
GO
CREATE FUNCTION GetDistances(@locations InTableType)
RETURNS OutTableType
AS
EXTERNAL NAME myCLRAssembly.GeoDistance.SQLCLRInitMethod
GO
/* Execution */
DECLARE @myTable InTableType
INSERT INTO @myTable(LocationName, Lat, Lon) VALUES('aaa', -50.0, -20.0)
INSERT INTO @myTable(LocationName, Lat, Lon) VALUES('bbb', -20.0, -50.0)
SELECT * FROM @myTable
DECLARE @myResult OutTableType
INSERT INTO @myResult
GetDistances @myTable /* SQLCLR Call: GeoDistance.SQLCLRInitMethod(@myTable) */
The lat/lon -> distance thing is a silly example that should of course be better handled entirely in SQL; but I hope it illustrates the general intent of table-in -> table-out through a table-valued UDF tied to a SQLCLR assembly.
I am not certain this is possible; what would the SQLCLRInitMethod method signature look like in the C#?
public class GeoDistance
{
[SqlFunction(FillRowMethodName = "FillRow")]
public static IEnumerable SQLCLRInitMethod(<appropriateType> myInputData)
{
//...
}
public static void FillRow(...)
{
//...
}
}
If it's not possible, I know I can use a "context connection=true" SQL connection within the C# code to have the CLR component query for the necessary data given the relevant keys; but that's sensitive to changes in the DB schema. So I hope to just have SQL bundle up all the source data and pass it to the function.
Bonus question - assuming this works at all, would it also work with more than one input table?
Upvotes: 10
Views: 13776
Reputation: 48826
This question seems to be (mostly) a duplicate of:
CLR Table-valued function with array argument
As a quick note, in that question I recommended: delimited list, XML, or CLR UDT.
There is also the option of filling a table and loading the DataTable from it in the function. Using a real Table is likely not recommended as it would require extra effort to make it "thread safe" (to not cross data with other SPIDs) and would require an additional clean-up process since the Function would not be able to do a DML statement to clean it up once it was done with the data. In certain situations maybe this is preferred but probably not for this particular case. Fortunately, Temporary Tables are accessible within SQLCLR Functions (as read-only, but they are not accessible at all in T-SQL functions). Using Temp Tables would have the same advantages as using permanent Tables but not the disadvantages of collisions with other SPIDs or needing to be cleaned up separately. The only requirement is that you use the Context Connection as that is the only way to access session-based objects (i.e. Temp Tables).
So for this particular case, I would recommend trying either the Temp Table or XML options.
Upvotes: 3
Reputation: 4468
Turns out that there's a fixed list of valid inputs on a SQLCLR function, determined by the available mapping between .NET datatypes and SQL datatypes
SQL Datatype "table" is explicitly called out as having no mapping through the CLR.
Ergo, it's not possible to pass table-valued data INTO a table-valued CLR function as method parameters.
Alternatives
It does seem possible to get tabular data in via select ... for xml
contortions to feed into a SqlXml
parameter.
I have successfully used SqlConnection conn = new SqlConnection("context connection = true");
in the .NET code to let the TVF query the DB for the tabular data it needs.
Upvotes: 10