SQL Server - Table Functions
Table functions are Transact-SQL functions that return a table. They are a powerful way to encapsulate complex queries and promote code reusability.
Overview
Table-valued functions (TVFs) allow you to define functions that return a result set, similar to a view, but with the added benefit of accepting input parameters. This makes them highly flexible for scenarios where you need to filter or customize the data returned based on specific criteria.
There are two types of table-valued functions:
- Inline Table-Valued Functions (ITVF): These functions contain a single `SELECT` statement and are often more performant than multi-statement TVFs due to their ability to be expanded in the query optimizer.
- Multi-Statement Table-Valued Functions (MSTVF): These functions can contain multiple Transact-SQL statements, including control-of-flow logic, and a `RETURN` statement to return a table variable.
Syntax
Inline Table-Valued Function
The basic syntax for an inline table-valued function is:
CREATE FUNCTION schema_name.function_name
(
@parameter1 datatype,
@parameter2 datatype
)
RETURNS TABLE
AS
RETURN
(
-- Single SELECT statement that defines the table to be returned
SELECT column1, column2, ...
FROM your_table
WHERE some_condition
);
Multi-Statement Table-Valued Function
The basic syntax for a multi-statement table-valued function is:
CREATE FUNCTION schema_name.function_name
(
@parameter1 datatype,
@parameter2 datatype
)
RETURNS @table_variable TABLE
(
column1 datatype,
column2 datatype,
...
)
AS
BEGIN
-- Transact-SQL statements
INSERT INTO @table_variable (column1, column2, ...)
SELECT columnA, columnB, ...
FROM another_table
WHERE condition1;
-- More statements can follow...
RETURN;
END;
Common Use Cases
- Parameterized Views: Create dynamic result sets based on input parameters.
- Data Filtering and Aggregation: Encapsulate complex filtering or aggregation logic for specific data subsets.
- Code Reusability: Avoid repeating the same query logic across multiple parts of your application.
- Simplifying Complex Queries: Break down intricate queries into smaller, manageable, and reusable functions.
Examples
Example 1: Inline TVF to get customers by region
This function returns a table of customers residing in a specified region.
-- Assume a 'Customers' table with columns like CustomerID, Name, Region
CREATE FUNCTION Sales.GetCustomersByRegion
(
@RegionName NVARCHAR(50)
)
RETURNS TABLE
AS
RETURN
(
SELECT CustomerID, Name
FROM Sales.Customers
WHERE Region = @RegionName
);
GO
-- How to use it:
SELECT * FROM Sales.GetCustomersByRegion('North America');
Example 2: Multi-Statement TVF to calculate order totals
This function calculates the total price for each order, including discounts, and returns it as a table.
-- Assume 'Orders' table (OrderID, OrderDate) and 'OrderDetails' table (OrderID, ProductID, Quantity, UnitPrice, Discount)
CREATE FUNCTION Sales.CalculateOrderTotals
(
@OrderID INT
)
RETURNS @OrderTotals TABLE
(
OrderID INT,
TotalPrice DECIMAL(18, 2)
)
AS
BEGIN
INSERT INTO @OrderTotals (OrderID, TotalPrice)
SELECT
od.OrderID,
SUM(od.Quantity * od.UnitPrice * (1 - od.Discount)) AS CalculatedPrice
FROM Sales.OrderDetails AS od
WHERE od.OrderID = @OrderID
GROUP BY od.OrderID;
RETURN;
END;
GO
-- How to use it:
SELECT * FROM Sales.CalculateOrderTotals(10248);
Performance Considerations
- ITVF vs. MSTVF: In most cases, ITVF offer better performance because the query optimizer can expand them directly into the calling query, allowing for better optimization. MSTVFs return a table variable, which can sometimes hinder optimization.
- Indexing: Ensure that the underlying tables used by your TVFs are properly indexed to improve query performance.
- Parameter Sniffing: Be aware of parameter sniffing issues, especially with MSTVFs, which might lead to suboptimal execution plans for different parameter values.
Best Practices
- Use descriptive names for your functions.
- Add comments to explain the logic, especially for MSTVFs.
- Test your functions thoroughly with various inputs.
- Prefer ITVF for simplicity and performance when possible.
- Keep functions focused on a single logical task.
When designing TVFs, consider the data size and query patterns to choose between inline and multi-statement approaches for optimal performance.