User-defined functions for T-SQL
This topic provides reference information about User-Defined Functions (UDFs) in SQL Server and their compatibility with PostgreSQL. It introduces the types of UDFs supported in SQL Server, including scalar functions, table-valued functions, and multi-statement table-valued functions. The topic explains the characteristics of UDFs, such as their inability to modify database structures or data outside their scope, and the distinction between deterministic and non-deterministic functions.
Feature compatibility | AWS SCT / AWS DMS automation level | AWS SCT action code index | Key differences |
---|---|---|---|
|
|
N/A |
Syntax and option differences. |
SQL Server Usage
User-Defined Functions (UDF) are code objects that accept input parameters and return either a scalar value or a set consisting of rows and columns. You can use T-SQL or Common Language Runtime (CLR) code to implement SQL Server UDFs.
Note
This section doesn’t cover CLR code objects.
Function invocations can’t have any lasting impact on the database. They must be contained and can only modify objects and data local to their scope (for example, data in local variables). Functions aren’t allowed to modify data or the structure of a database.
Functions may be deterministic or non-deterministic. Deterministic functions always return the same result when you run them with the same data. Non-deterministic functions may return different results each time they run. For example, a function that returns the current date or time.
SQL Server supports three types of T-SQL UDFs: Scalar Functions, Table-Valued Functions, and Multi-Statement Table-Valued Functions.
SQL Server 2019 adds scalar user-defined functions (UDF) inlining. Inlining transforms functions into relational expressions and embeds them in the calling SQL query. This transformation improves the performance of workloads that take advantage of scalar UDFs. Scalar UDF inlining facilitates cost-based optimization of operations inside UDFs. The results are efficient, set-oriented, and parallel instead of inefficient, iterative, serial run plans. For more information, see Scalar UDF Inlining
Scalar User-Defined Functions
Scalar UDFs accept zero or more parameters and return a scalar value. You can use scalar UDFs in T-SQL expressions.
Syntax
CREATE FUNCTION <Function Name> ([{<Parameter Name> [AS] <Data Type> [= <Default
Value>] [READONLY]} [,...n]])
RETURNS <Return Data Type>
[AS]
BEGIN
<Function Body Code>
RETURN <Scalar Expression>
END[;]
Examples
The following example creates a scalar function to change the first character of a string to upper case.
CREATE FUNCTION dbo.UpperCaseFirstChar (@String VARCHAR(20)) RETURNS VARCHAR(20) AS BEGIN RETURN UPPER(LEFT(@String, 1)) + LOWER(SUBSTRING(@String, 2, 19)) END;
SELECT dbo.UpperCaseFirstChar ('mIxEdCasE'); Mixedcase
User-Defined Table-Valued Functions
Inline table-valued UDFs are similar to views or a Common Table Expressions (CTE) with the added benefit of parameters. You can use inline table-valued UDFs in FROM
clauses as subqueries. Also, you can join inline table-valued UDFs to other source table rows using the APPLY
and OUTER APPLY
operators. In-line table-valued UDFs have many associated internal optimizer optimizations due to their simple, view-like characteristics.
Syntax
CREATE FUNCTION <Function Name> ([{<Parameter Name> [AS] <Data Type> [= <Default
Value>] [READONLY]} [,...n]])
RETURNS TABLE
[AS]
RETURN (<SELECT Query>)[;]
Examples
The following example creates a table-valued function to aggregate employee orders.
CREATE TABLE Orders ( OrderID INT NOT NULL PRIMARY KEY, EmployeeID INT NOT NULL, OrderDate DATETIME NOT NULL );
INSERT INTO Orders (OrderID, EmployeeID, OrderDate) VALUES (1, 1, '20180101 13:00:05'), (2, 1, '20180201 11:33:12'), (3, 2, '20180112 10:22:35');
CREATE FUNCTION dbo.EmployeeMonthlyOrders (@EmployeeID INT) RETURNS TABLE AS RETURN ( SELECT EmployeeID, YEAR(OrderDate) AS OrderYear, MONTH(OrderDate) AS OrderMonth, COUNT(*) AS NumOrders FROM Orders AS O WHERE EmployeeID = @EmployeeID GROUP BY EmployeeID, YEAR(OrderDate), MONTH(OrderDate) );
SELECT * FROM dbo.EmployeeMonthlyOrders (1) EmployeeID OrderYear OrderMonth NumOrders 1 2018 1 1 1 2018 2 1
Multi-Statement User-Defined Table-Valued Functions
Multi-statement table-valued UDFs, such as In-line UDFs, are also similar to views or CTEs with the added benefit of parameters. You can use multi-statement table-valued UDFs in FROM
clauses as sub queries. Also, you can join multi-statement table-valued UDFs to other source table rows using the APPLY
and OUTER APPLY
operators.
The difference between multi-statement UDFs and the inline UDFs is that multi-statement UDFs aren’t restricted to a single SELECT
statement. They can consist of multiple statements including logic implemented with flow control, complex data processing, security checks, and so on.
The downside of using multi-statement UDFs is that there are far less optimizations possible and performance may suffer.
Syntax
CREATE FUNCTION <Function Name> ([{<Parameter Name> [AS] <Data Type> [= <Default
Value>] [READONLY]} [,...n]])
RETURNS <@Return Variable> TABLE <Table Definition>
[AS]
BEGIN
<Function Body Code>
RETURN
END[;]
For more information, see CREATE FUNCTION (Transact-SQL)
PostgreSQL Usage
For more information, see Stored Procedures.
Syntax
CREATE [ OR REPLACE ] FUNCTION name ( [ [ argmode ] [ argname ] argtype [ { DEFAULT | = } default_expr ] [, ...]] ) [ RETURNS rettype | RETURNS TABLE ( column_name column_type [, ...] ) ] { LANGUAGE lang_name | TRANSFORM { FOR TYPE type_name } [, ... ] | WINDOW | IMMUTABLE | STABLE | VOLATILE | [ NOT ] LEAKPROOF | CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT | [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER | PARALLEL { UNSAFE | RESTRICTED | SAFE } | COST execution_cost | ROWS result_rows | SET configuration_parameter { TO value | = value | FROM CURRENT } | AS 'definition' | AS 'obj_file', 'link_symbol' } ... [ WITH ( attribute [, ...] ) ]