Create an Inline Table-Valued Function (ITVF) in SQL Server

You can create an inline table-valued function (ITVF) in SQL Server using the T-SQL CREATE FUNCTION syntax.

Syntax

Here’s the official syntax for inline TVFs.

CREATE [ OR ALTER ] FUNCTION [ schema_name. ] function_name   
( [ { @parameter_name [ AS ] [ type_schema_name. ] parameter_data_type   
    [ = default ] [ READONLY ] }   
    [ ,...n ]  
  ]  
)  
RETURNS TABLE  
    [ WITH <function_option> [ ,...n ] ]  
    [ AS ]  
    RETURN [ ( ] select_stmt [ ) ]  
[ ; ]  

Example 1 – Basic ITVF

Here’s an example of a basic inline table-valued function.

CREATE FUNCTION dbo.udf_CatsByName_ITVF( @CatName varchar(70) )
    RETURNS TABLE
AS
RETURN (
    SELECT 
        CatId,
        CatName,
        Phone
    FROM dbo.Cats
    WHERE CatName = @CatName
    );

GO

In this case, the function requires a cat name be passed in as an argument. It then uses this argument in the query in order to return the relevant data.

Example 2 – Add Schema Binding

It’s usually a good idea to schema bind your functions by using the SCHEMABINDING argument.

Doing this will ensure that the underlying tables can’t be changed in a way that would affect your function.

Without schema binding, the underlying tables could be modified or even deleted. Doing this could break the function.

Here’s the same function, but this time with schema binding:

CREATE FUNCTION dbo.udf_CatsByName_ITVF( @CatName varchar(70) )
    RETURNS TABLE
    WITH SCHEMABINDING
AS
RETURN (
    SELECT 
        CatId,
        CatName,
        Phone
    FROM dbo.Cats
    WHERE CatName = @CatName
    );

GO

Notice I used the two-part name when referencing the table in my query (I used dbo.Cats when referencing the table, instead of just Cats). Doing this is a requirement for schema binding an object. If you try to schema bind an object without using two-part names you’ll get an error.

Now that I’ve schema bound my function, if I try to drop the table referenced in its definition, I get an error:

DROP TABLE Cats;

Result:

Msg 3729, Level 16, State 1, Line 1
Cannot DROP TABLE 'cats' because it is being referenced by object 'udf_CatsByName_ITVF'.

By the way, here’s what happens if I try to create the function without using two-part naming:

CREATE FUNCTION dbo.udf_CatsByName_ITVF( @CatName varchar(70) )
    RETURNS TABLE
    WITH SCHEMABINDING
AS
RETURN (
    SELECT 
        CatId,
        CatName,
        Phone
    FROM Cats
    WHERE CatName = @CatName
    );

GO

Result:

Msg 4512, Level 16, State 3, Procedure udf_CatsByName_ITVF, Line 7
Cannot schema bind table valued function 'dbo.udf_CatsByName_ITVF' because name 'Cats' is invalid for schema binding. Names must be in two-part format and an object cannot reference itself.

Example 3 – Add Encryption

You can also encrypt your functions using the ENCRYPTION argument.

Here’s an example of encrypting the function:

CREATE FUNCTION dbo.udf_CatsByName_ITVF( @CatName varchar(70) )
    RETURNS TABLE
    WITH SCHEMABINDING, ENCRYPTION
AS
RETURN (
    SELECT 
        CatId,
        CatName,
        Phone
    FROM dbo.Cats
    WHERE CatName = @CatName
    );

GO

Now I’m unable to view the function’s definition.

SELECT definition 
FROM sys.sql_modules
WHERE object_id = OBJECT_ID('udf_CatsByName_ITVF');

Result:

+--------------+
| definition   |
|--------------|
| NULL         |
+--------------+

I also get an error message when trying to script the function’s definition via Azure Data Studio:

No script was returned when scripting as Create on object UserDefinedFunction

Note that an encrypted function’s text is still available to privileged users that can either access system tables over the DAC port or directly access database files. Also, users that can attach a debugger to the server process can retrieve the original procedure from memory at runtime.