Table-Valued Functions (TVFs)
Table-Valued Functions (TVFs) are a type of Transact-SQL (T-SQL) function that returns a table. They offer a powerful way to encapsulate complex logic, improve code reusability, and enhance query performance. TVFs can be used in place of tables in many T-SQL statements, such as the FROM clause of a query, or as arguments to other functions.
There are two main types of Table-Valued Functions:
- Inline Table-Valued Functions (ITVFs)
- Multi-Statement Table-Valued Functions (MSTVFs)
Inline Table-Valued Functions (ITVFs)
Inline Table-Valued Functions are simpler and often more performant than Multi-Statement TVFs. They are defined by a single SELECT statement.
Syntax
CREATE FUNCTION schema_name.function_name
( [ @parameter [ datatype ] [ = default ] [ OUT | OUTPUT ] ]
[ ,...n ]
)
RETURNS TABLE
[ WITH <function_option> [<function_option>,...n] ]
AS
RETURN
(
SELECT select_list
FROM table_sources
[ WHERE search_conditions ]
[ GROUP BY ... ]
[ HAVING ... ]
[ ORDER BY ... ]
)
[;]<function_option> ::=
| SCHEMABINDING
| <return_table_type_definition>
<return_table_type_definition> ::=
TABLE ( column_name data_type [ column_constraint ] [ ,...n ] )
| CHECK | ENCRYPTION | EXECUTE AS | INSTEAD OF | ROWCOUNT | TRANSPARENTDATAENCRYPTION
Example
Get Employee Details by Department
This function returns a table of employees belonging to a specified department.
CREATE FUNCTION dbo.GetEmployeesByDepartment (@DepartmentName NVARCHAR(50))
RETURNS TABLE
AS
RETURN
(
SELECT
E.EmployeeID,
E.FirstName,
E.LastName,
D.DepartmentName
FROM
Employees AS E
JOIN
Departments AS D ON E.DepartmentID = D.DepartmentID
WHERE
D.DepartmentName = @DepartmentName
);
GO
-- Usage:
SELECT * FROM dbo.GetEmployeesByDepartment('Sales');
Multi-Statement Table-Valued Functions (MSTVFs)
Multi-Statement Table-Valued Functions allow for more complex logic, including multiple T-SQL statements, variable declarations, and conditional logic. The final result is populated into a table variable that is then returned.
Syntax
CREATE FUNCTION schema_name.function_name
( [ @parameter [ datatype ] [ = default ] [ OUT | OUTPUT ] ]
[ ,...n ]
)
RETURNS @return_variable TABLE
[ <return_table_type_definition> ]
[ WITH <function_option> [<function_option>,...n] ]
AS
BEGIN
-- T-SQL statements to populate @return_variable
RETURN
END
[;]<function_option> ::=
| SCHEMABINDING
| <return_table_type_definition>
<return_table_type_definition> ::=
TABLE ( column_name data_type [ column_constraint ] [ ,...n ] )
| CHECK | ENCRYPTION | EXECUTE AS | INSTEAD OF | ROWCOUNT | TRANSPARENTDATAENCRYPTION
Example
Calculate Employee Salary with Bonus
This function calculates the total salary for employees, including a potential bonus based on performance.
CREATE FUNCTION dbo.CalculateEmployeeTotalSalary (@EmployeeID INT)
RETURNS @EmployeeSalaries TABLE
(
EmployeeID INT PRIMARY KEY,
BaseSalary DECIMAL(10, 2),
Bonus DECIMAL(10, 2),
TotalSalary DECIMAL(10, 2)
)
AS
BEGIN
DECLARE @BaseSalary DECIMAL(10, 2);
DECLARE @Bonus DECIMAL(10, 2) = 0;
SELECT @BaseSalary = Salary
FROM Employees
WHERE EmployeeID = @EmployeeID;
-- Simulate bonus calculation based on performance
IF EXISTS (SELECT 1 FROM PerformanceReviews WHERE EmployeeID = @EmployeeID AND Rating > 4)
BEGIN
SET @Bonus = @BaseSalary * 0.10; -- 10% bonus
END
INSERT INTO @EmployeeSalaries (EmployeeID, BaseSalary, Bonus, TotalSalary)
VALUES (@EmployeeID, @BaseSalary, @Bonus, @BaseSalary + @Bonus);
RETURN;
END
GO
-- Usage:
SELECT * FROM dbo.CalculateEmployeeTotalSalary(101);
Key Differences and Considerations
| Feature | Inline TVF (ITVF) | Multi-Statement TVF (MSTVF) |
|---|---|---|
| Definition | Single SELECT statement |
Multiple T-SQL statements, including variable declarations and control flow |
| Performance | Generally better optimization by the query optimizer, as it can be expanded in-line | Can sometimes be less performant as the optimizer treats them more like black boxes. Results are materialized into a table variable first. |
| Complexity | Simpler logic | Supports more complex business logic and procedural operations |
| Use Cases | Simple data retrieval, filtering, and transformations | Complex calculations, data aggregation across multiple steps, and procedural data manipulation |
SCHEMABINDING |
Supported | Not supported |
When to Use Table-Valued Functions
- Encapsulating Business Logic: Complex calculations, data transformations, or data retrieval logic that is used repeatedly.
- Improving Readability and Maintainability: Breaking down complex queries into smaller, manageable, and named units.
- Data Access Layer: Providing a consistent interface for accessing specific subsets or transformations of data.
- Security: Limiting user access to specific columns or rows by granting permissions on the TVF instead of the underlying tables.