Performance Impacts of Scalar Functions
By Tom Nonmacher
Scalar functions in SQL Server can have a significant impact on the performance of your database operations. They are a type of function that return a single value, rather than a table. This can be useful in certain situations, but it also has some drawbacks.
Scalar functions in SQL Server 2016 and 2017, MySQL 5.7, DB2 11.1, and Azure SQL are known to cause query performance issues because they are executed once for each row of data. This means that if your query returns a large number of rows, the scalar function will be executed that many times, which can slow down your query considerably.
Consider this T-SQL example:
SELECT CustomerID, OrderID, OrderDate, dbo.GetOrderTotal(OrderID) AS OrderTotal
FROM Sales.Orders
WHERE dbo.GetOrderTotal(OrderID) > 100;
In this example, the scalar function GetOrderTotal() is called twice for each row in the Sales.Orders table. This can result in a significant performance hit, particularly if the table contains a large number of rows.
One way to improve the performance of queries that include scalar functions is to use a derived table or a common table expression (CTE) to avoid calling the function multiple times. Here's an example in MySQL:
WITH OrderTotals AS (
SELECT OrderID, GetOrderTotal(OrderID) AS OrderTotal
FROM Orders)
SELECT CustomerID, OrderID, OrderDate, OrderTotal
FROM Sales.Orders
JOIN OrderTotals ON Sales.Orders.OrderID = OrderTotals.OrderID
WHERE OrderTotal > 100;
In this example, the scalar function GetOrderTotal() is only called once for each row in the Orders table, which can significantly improve performance.
In DB2 11.1, you can use the MATERIALIZE function hint to force the evaluation of scalar functions only once per row, which can improve performance as shown in the following example:
SELECT CustomerID, OrderID, OrderDate, GetOrderTotal(OrderID) AS OrderTotal
FROM Sales.Orders
WHERE MATERIALIZE(GetOrderTotal(OrderID)) > 100;
This tells DB2 to evaluate the GetOrderTotal() function once per row and use the result in the WHERE clause.
In Azure SQL, you can use the APPLY operator to reduce the number of times a scalar function is called in a query. Here's an example:
SELECT CustomerID, OrderID, OrderDate, OrderTotal
FROM Sales.Orders
CROSS APPLY (SELECT dbo.GetOrderTotal(OrderID) AS OrderTotal) AS OT
WHERE OrderTotal > 100;
In this example, the scalar function GetOrderTotal() is only called once for each row in the Orders table, which can significantly improve performance.
Scalar functions can be a powerful tool in your SQL arsenal, but like any tool, they need to be used appropriately. By understanding how scalar functions work and how they can impact performance, you can make better decisions about when and how to use them.