User-Defined Functions in T-SQL

Write your own functions. A scalar function returns one value and works inside any query; a table-valued function returns rows and works like a table that takes parameters.

Journeyman · T-SQL · SQL · Updated

After this lesson you can

  • Create a scalar function and use it in a query
  • Create an inline table-valued function and query it in the FROM
  • Choose between a view, a stored procedure and a function

Key takeaways

  • A scalar function returns one value and works anywhere a value goes in a query. Call it with its schema, as in dbo.LineTotal(UnitPrice, Quantity, Discount).
  • In a function, RETURNS says the type it gives back, and RETURN gives back the value.
  • A scalar function runs once per row, so save it for a formula worth sharing, not every calculation.
  • An inline table-valued function returns rows and goes in the FROM: a view that takes parameters.
  • Use a view to write a join once, a procedure for work with several steps or that changes data, and a function for a formula or a filtered table inside a query.

T-SQL comes with functions such as YEAR and ROUND. You can write your own, too: user-defined functions. Unlike a stored procedure, a function can be used inside a query.

A scalar function: one value

An order line's total, after its discount, is UnitPrice * Quantity * (1 - Discount). Every report that needs it has to get that formula right. A function writes it once:

CREATE OR ALTER FUNCTION dbo.LineTotal
(
    @UnitPrice money
    , @Quantity smallint
    , @Discount real
)
RETURNS money
AS
BEGIN
    RETURN @UnitPrice * @Quantity * (1 - @Discount);
END;
  • RETURNS says the type of the value it gives back.
  • RETURN gives back the value.
  • The name starts with the schema, dbo.: a scalar function must be called with it.

Use it like a built-in function, anywhere a value goes:

SELECT TOP (4) OrderId, ProductId, UnitPrice, Quantity, Discount
    , dbo.LineTotal(UnitPrice, Quantity, Discount) AS LineTotal
FROM OrderDetails
WHERE Discount > 0
ORDER BY OrderId, ProductId;
OrderId ProductId UnitPrice Quantity Discount LineTotal
10250 51 42.40 35 0.15 1261.40
10250 65 16.80 15 0.15 214.20
10251 22 16.80 6 0.05 95.76
10251 57 15.60 15 0.05 222.30

It works inside aggregates too:

SELECT TOP (3) OrderId, SUM(dbo.LineTotal(UnitPrice, Quantity, Discount)) AS OrderTotal
FROM OrderDetails
GROUP BY OrderId
ORDER BY OrderTotal DESC;
OrderId OrderTotal
10865 16387.50
10981 15810.00
11030 12615.05

A scalar function runs once per row, so on a big table it can be slow. Use one for a formula worth sharing, not for every calculation.

A table-valued function: rows

An inline table-valued function returns rows, so it's used in the FROM, like a table. Think of it as a view that takes parameters:

CREATE OR ALTER FUNCTION dbo.ProductsCheaperThan
(
    @Price money
)
RETURNS TABLE
AS
RETURN
    SELECT ProductId, ProductName, UnitPrice
    FROM Products
    WHERE UnitPrice < @Price;

RETURNS TABLE, then RETURN and a single SELECT: no BEGIN and END.

SELECT ProductName, UnitPrice
FROM dbo.ProductsCheaperThan(7)
ORDER BY UnitPrice;
ProductName UnitPrice
Geitost 2.50
Guaraná Fantástica 4.50
Konbu 6.00

Because it acts like a table, you can filter, join and group its rows like any other.

View, procedure or function?

View Stored procedure Function
Takes parameters No Yes Yes
Used inside a SELECT Yes, in the FROM No: run with EXEC Yes: scalar as a value, table-valued in the FROM
Can hold statements that change data No: one SELECT Yes No
Good for A join or filter written once Work with several steps, or that changes data A formula, or a filtered table, used inside queries

Check yourself

3 questions to try on the lesson page.

So what

Views name a query, procedures name a piece of work, and functions name a formula or a filtered table you can use inside other queries. Pick by what you need: parameters, changes, or a place inside a SELECT.

Key terms

  • User-Defined Function
  • Table-Valued Function
  • dbo Schema

Cheat sheets