How can I create a Table-Valued User-Defined Function in SQL Server that takes an input parameter for an IN clause?
I'm trying to write this very simple function:
CREATE FUNCTION dbo.MyTableValuedFunction (@myList VARCHAR(MAX))
RETURNS @myTable TABLE (
[ID] INT,
[Name] VARCHAR(MAX)
)
AS
BEGIN
INSERT INTO @myTable
SELECT ID, Name FROM MyTable WHERE ID IN (@myList)
RETURN
END
A popular generative AI is suggesting to build a sql string and use 'EXEC'
CREATE FUNCTION dbo.MyTableValuedFunction (@myList VARCHAR(MAX))
RETURNS @myTable TABLE (
[ID] INT,
[Name] VARCHAR(MAX)
)
AS
BEGIN
DECLARE @sql NVARCHAR(MAX)
SET @sql = 'SELECT ID, Name FROM MyTable WHERE ID IN (' + @myList + ')'
INSERT INTO @myTable
EXEC sp_executesql @sql
RETURN
END
But when I try to run it, I receive the following error: Invalid use of a side-effecting operator 'INSERT EXEC' within a function.
Is there any way to use an IN clause with a list as an input parameter in a Table-Valued User-Defined Function?
IN
expects either a list of scalar values or a SELECT
statement that returns a single column. You can't pass it a variable and then expect SQL Server to treat it as a tuple. IN (@ScalarVariable)
would be equivilent to = @ScalarVariable
and IN (@TableVariable)
would generate an error about an undefined scalar variable.
Instead, split your variable using STRING_SPLIT
. Also, switch to an inline table value function; they are far more performant to a multi-line table value function (as you've written above):
CREATE FUNCTION dbo.MyTableValuedFunction (@MyList VARCHAR(MAX))
RETURNS table AS
RETURN SELECT ID,
Name
FROM dbo.MyTable
WHERE ID IN (SELECT value
FROM STRING_SPLIT(@MyList,',')); --Comma (,) is assumed delimiter
GO
--Alternatively, use an `EXISTS`:
CREATE FUNCTION dbo.MyTableValuedFunction (@MyList VARCHAR(MAX))
RETURNS table AS
RETURN SELECT ID,
Name
FROM dbo.MyTable MT
WHERE EXISTS (SELECT 1
FROM STRING_SPLIT(@MyList,',') SS --Comma (,) is assumed delimiter
WHERE SS.value = MT.ID);
If a single ID can only appear in your variable (@MyList
) once, then you could also use a JOIN
:
CREATE FUNCTION dbo.MyTableValuedFunction (@MyList VARCHAR(MAX))
RETURNS table AS
RETURN SELECT ID,
Name
FROM dbo.MyTable MT
JOIN STRING_SPLIT(@MyList,',') SS ON MT.ID = SS.value;