User-Defined Functions that are implemented in Transact-SQL and return a single data value are referred to as T-SQL Scalar User-Defined Functions. T-SQL UDFs are an elegant way to achieve code reuse and modularity across SQL queries. Some computations (such as complex business rules) are easier to express in imperative UDF form. UDFs help in building up complex logic without requiring expertise in writing complex SQL queries. Scalar UDF inlining, a feature the Intelligent Query Processing suite of features. This feature improves the performance of queries that invoke scalar UDFs in SQL Server (starting with SQL Server 2019).
This example will show you how upgrading to Database Compatibility Level 150 could improve performance of queries which leverage scalar UDFs. More information about this feature is available here.
You could choose to use a container to evaluate this feature. Create an instance of SQL Server 2019 using a Docker image and restore the WideWorldImportersDW database backup
You will need the WideWorldImportersDW database for this exercise. If you don't have this sample database, then you download the sample database here.
Restore the copied WideWorldImportersDW database backup into the container and restore the backup.
docker pull mcr.microsoft.com/mssql/server:2019-latest
docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=`<A Strong Password`>" -p 1445:1433 --name sql2019demo -d mcr.microsoft.com/mssql/server:2019-latest
docker cp ".\Downloads\WideWorldImportersDW-Full.bak" sql2019demo:/var/opt/mssql/data
Note: For Linux installations the default path to use is /var/opt/mssql
USE [master]
GO
IF EXISTS (SELECT [database_id] FROM sys.databases WHERE [name] = 'WideWorldImportersDW')
ALTER DATABASE [WideWorldImportersDW] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
DECLARE @datafilepath VARCHAR(8000) = CAST(SERVERPROPERTY('InstanceDefaultDataPath') AS VARCHAR(4000)) + 'WideWorldImportersDW.mdf'
DECLARE @logfilepath VARCHAR(8000) = CAST(SERVERPROPERTY('InstanceDefaultLogPath') AS VARCHAR(4000)) + 'WideWorldImportersDW.ldf'
DECLARE @inmemfilepath VARCHAR(8000) = CAST(SERVERPROPERTY('InstanceDefaultDataPath') AS VARCHAR(4000)) + 'WideWorldImportersDW_InMemory_Data_1'
DECLARE @secondaryfilepath VARCHAR(8000) = CAST(SERVERPROPERTY('InstanceDefaultDataPath') AS VARCHAR(4000))+ 'WideWorldImportersDW_2.ndf'
-- Change @backupfile file path as needed
DECLARE @backupfile VARCHAR(8000) = 'E:\SampleDBs\WideWorldImportersDW-Full.bak'
RESTORE DATABASE WideWorldImportersDW
FROM DISK = @backupfile
WITH MOVE 'WWI_Primary' TO @datafilepath,
MOVE 'WWI_UserData' TO @secondaryfilepath,
MOVE 'WWIDW_InMemory_Data_1' TO @inmemfilepath,
MOVE 'WWI_Log' TO @logfilepath, NOUNLOAD, REPLACE, STATS = 10
GO
USE [master]
GO
ALTER DATABASE [WideWorldImportersDW] MODIFY FILE ( NAME = N'WWI_Log', SIZE = 4GB )
GO
USE WideWorldImportersDW;
GO
/*
Assumes a fresh restore of WideWorldImportersDW
*/
IF OBJECT_ID('Fact.OrderHistory') IS NULL
BEGIN
SELECT [Order Key], [City Key], [Customer Key], [Stock Item Key], [Order Date Key], [Picked Date Key], [Salesperson Key], [Picker Key], [WWI Order ID], [WWI Backorder ID], Description, Package, Quantity, [Unit Price], [Tax Rate], [Total Excluding Tax], [Tax Amount], [Total Including Tax], [Lineage Key]
INTO Fact.OrderHistory
FROM Fact.[Order];
END;
ALTER TABLE Fact.OrderHistory
ADD CONSTRAINT PK_Fact_OrderHistory PRIMARY KEY NONCLUSTERED ([Order Key] ASC, [Order Date Key] ASC) WITH (DATA_COMPRESSION = PAGE);
GO
CREATE INDEX IX_Stock_Item_Key
ON Fact.OrderHistory ([Stock Item Key])
INCLUDE(Quantity)
WITH (DATA_COMPRESSION = PAGE);
GO
CREATE INDEX IX_OrderHistory_Quantity
ON Fact.OrderHistory ([Quantity])
INCLUDE([Order Key])
WITH (DATA_COMPRESSION = PAGE);
GO
CREATE INDEX IX_OrderHistory_CustomerKey
ON Fact.OrderHistory([Customer Key])
INCLUDE ([Total Including Tax])
WITH (DATA_COMPRESSION = PAGE);
GO
IF (SELECT COUNT(*) FROM [Fact].[OrderHistory]) < 3702592
BEGIN
DECLARE @i smallint
SET @i = 0
WHILE @i < 4
BEGIN
INSERT INTO [Fact].[OrderHistory] ([City Key], [Customer Key], [Stock Item Key], [Order Date Key], [Picked Date Key], [Salesperson Key], [Picker Key], [WWI Order ID], [WWI Backorder ID], Description, Package, Quantity, [Unit Price], [Tax Rate], [Total Excluding Tax], [Tax Amount], [Total Including Tax], [Lineage Key])
SELECT [City Key], [Customer Key], [Stock Item Key], [Order Date Key], [Picked Date Key], [Salesperson Key], [Picker Key], [WWI Order ID], [WWI Backorder ID], Description, Package, Quantity, [Unit Price], [Tax Rate], [Total Excluding Tax], [Tax Amount], [Total Including Tax], [Lineage Key]
FROM [Fact].[OrderHistory];
SET @i = @i +1
END;
END
GO
IF OBJECT_ID('Fact.OrderHistoryExtended') IS NULL
BEGIN
SELECT [Order Key], [City Key], [Customer Key], [Stock Item Key], [Order Date Key], [Picked Date Key], [Salesperson Key], [Picker Key], [WWI Order ID], [WWI Backorder ID], Description, Package, Quantity, [Unit Price], [Tax Rate], [Total Excluding Tax], [Tax Amount], [Total Including Tax], [Lineage Key]
INTO Fact.OrderHistoryExtended
FROM Fact.[OrderHistory];
END;
ALTER TABLE Fact.OrderHistoryExtended
ADD CONSTRAINT PK_Fact_OrderHistoryExtended PRIMARY KEY NONCLUSTERED ([Order Key] ASC, [Order Date Key] ASC)
WITH (DATA_COMPRESSION = PAGE);
GO
CREATE INDEX IX_Stock_Item_Key
ON Fact.OrderHistoryExtended ([Stock Item Key])
INCLUDE (Quantity);
GO
IF (SELECT COUNT(*) FROM [Fact].[OrderHistory]) < 29620736
BEGIN
DECLARE @i smallint
SET @i = 0
WHILE @i < 3
BEGIN
INSERT Fact.OrderHistoryExtended([City Key], [Customer Key], [Stock Item Key], [Order Date Key], [Picked Date Key], [Salesperson Key], [Picker Key], [WWI Order ID], [WWI Backorder ID], Description, Package, Quantity, [Unit Price], [Tax Rate], [Total Excluding Tax], [Tax Amount], [Total Including Tax], [Lineage Key])
SELECT [City Key], [Customer Key], [Stock Item Key], [Order Date Key], [Picked Date Key], [Salesperson Key], [Picker Key], [WWI Order ID], [WWI Backorder ID], Description, Package, Quantity, [Unit Price], [Tax Rate], [Total Excluding Tax], [Tax Amount], [Total Including Tax], [Lineage Key]
FROM Fact.OrderHistoryExtended;
SET @i = @i +1
END;
END
GO
UPDATE Fact.OrderHistoryExtended
SET [WWI Order ID] = [Order Key];
GO
-- Repeat the following until log shrinks. These demos don't require much log space.
CHECKPOINT
GO
DBCC SHRINKFILE (N'WWI_Log' , 0, TRUNCATEONLY)
GO
SELECT * FROM sys.dm_db_log_space_usage
GO
USE [WideWorldImportersDW];
GO
CREATE OR ALTER FUNCTION dbo.ufn_customer_category(@CustomerKey INT)
RETURNS CHAR(10) AS
BEGIN
DECLARE @total_amount DECIMAL(18,2);
DECLARE @category CHAR(10);
SELECT @total_amount =
SUM([Total Including Tax])
FROM [Fact].[OrderHistory]
WHERE [Customer Key] = @CustomerKey;
IF @total_amount < 500000
SET @category = 'REGULAR';
ELSE IF @total_amount < 1000000
SET @category = 'GOLD';
ELSE
SET @category = 'PLATINUM';
RETURN @category;
END
GO
In the result set, scroll to the left and notice the value of the is_inlineable column. The value 1 shows that the UDF has inlineable constructs, meaning imperative statements that can be expressed in relational expressions. For example, an IF <expression> ELSE
statement can be expressed as an inline SELECT CASE WHEN <expression> THE 1 ELSE 0 END AS <column>
statement, or a SET @var = <expression>
can be inlined as SELECT <expression> AS var
.
The relational expressions for imperative statements handled by the Scalar UDF Inlining feature can be found in the “Froid: Optimization of Imperative Programs in a Relational Database” paper by Microsoft’s Gray Systems Lab in Madison Wisconsin.
USE [WideWorldImportersDW];
GO
SELECT * FROM sys.sql_modules
WHERE object_id = OBJECT_ID('ufn_customer_category')
GO
object_id | definition | uses_ansi_nulls | uses_quoted_identifier | is_schema_bound | uses_database_collation | is_recompiled | null_on_null_input | execute_as_principal_id | uses_native_compilation | inline_type | is_inlineable |
---|---|---|---|---|---|---|---|---|---|---|---|
1954106002 | CREATE FUNCTION dbo.ufn_customer_category(@CustomerKey INT) RETURNS CHAR(10) AS BEGIN DECLARE @total_amount DECIMAL(18,2); DECLARE @category CHAR(10); SELECT @total_amount = SUM([Total Including Tax]) FROM [Fact].[OrderHistory] WHERE [Customer Key] = @CustomerKey; IF @total_amount < 500000 SET @category = 'REGULAR'; ELSE IF @total_amount < 1000000 SET @category = 'GOLD'; ELSE SET @category = 'PLATINUM'; RETURN @category; END | 1 | 1 | 0 | 0 | 0 | 0 | NULL | 0 | 1 | 1 |
Even when the database compatibility level is set to the defauult (150), this can be done by using the USE HINT DISABLE_TSQL_SCALAR_UDF_INLINING query hint to disable the feature.
USE [WideWorldImportersDW];
GO
ALTER DATABASE [WideWorldImportersDW] SET COMPATIBILITY_LEVEL = 150;
GO
ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;
GO
SELECT TOP 100 [Customer Key], [Customer],
dbo.ufn_customer_category([Customer Key]) AS [Discount Price]
FROM [Dimension].[Customer]
ORDER BY [Customer Key]
OPTION (RECOMPILE,USE HINT('DISABLE_TSQL_SCALAR_UDF_INLINING'));
GO
Customer Key | Customer | Discount Price |
---|---|---|
0 | Unknown | PLATINUM |
1 | Tailspin Toys (Head Office) | PLATINUM |
2 | Tailspin Toys (Sylvanite, MT) | PLATINUM |
3 | Tailspin Toys (Peeples Valley, AZ) | PLATINUM |
4 | Tailspin Toys (Medicine Lodge, KS) | PLATINUM |
5 | Tailspin Toys (Gasport, NY) | PLATINUM |
6 | Tailspin Toys (Jessie, ND) | PLATINUM |
7 | Tailspin Toys (Frankewing, TN) | PLATINUM |
8 | Tailspin Toys (Bow Mar, CO) | PLATINUM |
9 | Tailspin Toys (Netcong, NJ) | PLATINUM |
10 | Tailspin Toys (Wimbledon, ND) | PLATINUM |
11 | Tailspin Toys (Devault, PA) | PLATINUM |
12 | Tailspin Toys (Biscay, MN) | PLATINUM |
13 | Tailspin Toys (Stonefort, IL) | PLATINUM |
14 | Tailspin Toys (Long Meadow, MD) | PLATINUM |
15 | Tailspin Toys (Batson, TX) | PLATINUM |
16 | Tailspin Toys (Coney Island, MO) | PLATINUM |
17 | Tailspin Toys (East Fultonham, OH) | PLATINUM |
18 | Tailspin Toys (Goffstown, NH) | PLATINUM |
19 | Tailspin Toys (Lemeta, AK) | PLATINUM |
20 | Tailspin Toys (College Place, WA) | PLATINUM |
21 | Tailspin Toys (Tresckow, PA) | PLATINUM |
22 | Tailspin Toys (Ward Ridge, FL) | PLATINUM |
23 | Tailspin Toys (Ikatan, AK) | PLATINUM |
24 | Tailspin Toys (Dundarrach, NC) | PLATINUM |
25 | Tailspin Toys (Avenal, CA) | PLATINUM |
26 | Tailspin Toys (Hedrick, IA) | PLATINUM |
27 | Tailspin Toys (Bowlus, MN) | PLATINUM |
28 | Tailspin Toys (North Ridge, NY) | PLATINUM |
29 | Tailspin Toys (Eulaton, AL) | PLATINUM |
30 | Tailspin Toys (Koontzville, WA) | PLATINUM |
31 | Tailspin Toys (Lake Stevens, WA) | PLATINUM |
32 | Tailspin Toys (Maypearl, TX) | PLATINUM |
33 | Tailspin Toys (Boyden Arbor, SC) | PLATINUM |
34 | Tailspin Toys (Dallardsville, TX) | PLATINUM |
35 | Tailspin Toys (Slanesville, WV) | PLATINUM |
36 | Tailspin Toys (Ekron, KY) | PLATINUM |
37 | Tailspin Toys (Kerby, OR) | PLATINUM |
38 | Tailspin Toys (Airport Drive, MO) | PLATINUM |
39 | Tailspin Toys (Diablock, KY) | PLATINUM |
40 | Tailspin Toys (Impact, TX) | PLATINUM |
41 | Tailspin Toys (Olivette, MO) | PLATINUM |
42 | Tailspin Toys (Arietta, NY) | PLATINUM |
43 | Tailspin Toys (Upper Preston, WA) | PLATINUM |
44 | Tailspin Toys (Amanda Park, WA) | PLATINUM |
45 | Tailspin Toys (Severna Park, MD) | PLATINUM |
46 | Tailspin Toys (Jemison, AL) | PLATINUM |
47 | Tailspin Toys (Lake Hughes, CA) | PLATINUM |
48 | Tailspin Toys (Trentwood, WA) | PLATINUM |
49 | Tailspin Toys (Muir, MI) | PLATINUM |
50 | Tailspin Toys (Hambleton, WV) | PLATINUM |
51 | Tailspin Toys (Imlaystown, NJ) | PLATINUM |
52 | Tailspin Toys (Idria, CA) | PLATINUM |
53 | Tailspin Toys (Nanafalia, AL) | PLATINUM |
54 | Tailspin Toys (Railroad, PA) | PLATINUM |
55 | Tailspin Toys (Gum Branch, GA) | PLATINUM |
56 | Tailspin Toys (Royal City, WA) | PLATINUM |
57 | Tailspin Toys (Lakemore, OH) | PLATINUM |
58 | Tailspin Toys (Mappsburg, VA) | PLATINUM |
59 | Tailspin Toys (Edgartown, MA) | PLATINUM |
60 | Tailspin Toys (East Dailey, WV) | PLATINUM |
61 | Tailspin Toys (Fairfield Glade, TN) | PLATINUM |
62 | Tailspin Toys (Great Neck Estates, NY) | PLATINUM |
63 | Tailspin Toys (Stallion Springs, CA) | PLATINUM |
64 | Tailspin Toys (Hodgdon, ME) | PLATINUM |
65 | Tailspin Toys (North Cowden, TX) | PLATINUM |
66 | Tailspin Toys (Madaket, MA) | PLATINUM |
67 | Tailspin Toys (Sentinel Butte, ND) | PLATINUM |
68 | Tailspin Toys (Gardere, LA) | PLATINUM |
69 | Tailspin Toys (Lytle Creek, CA) | PLATINUM |
70 | Tailspin Toys (New Baden, IL) | PLATINUM |
71 | Tailspin Toys (Good Hart, MI) | PLATINUM |
72 | Tailspin Toys (Cortaro, AZ) | PLATINUM |
73 | Tailspin Toys (Cokato, MN) | PLATINUM |
74 | Tailspin Toys (Indios, PR) | PLATINUM |
75 | Tailspin Toys (Windsor Locks, CT) | PLATINUM |
76 | Tailspin Toys (Yewed, OK) | PLATINUM |
77 | Tailspin Toys (Hahira, GA) | PLATINUM |
78 | Tailspin Toys (Sekiu, WA) | PLATINUM |
79 | Tailspin Toys (Page City, KS) | PLATINUM |
80 | Tailspin Toys (Valdese, NC) | PLATINUM |
81 | Tailspin Toys (Big Moose, NY) | PLATINUM |
82 | Tailspin Toys (La Cueva, NM) | PLATINUM |
83 | Tailspin Toys (Absecon, NJ) | PLATINUM |
84 | Tailspin Toys (Aceitunas, PR) | PLATINUM |
85 | Tailspin Toys (Andrix, CO) | PLATINUM |
86 | Tailspin Toys (New Lexington, OH) | PLATINUM |
87 | Tailspin Toys (Sauquoit, NY) | PLATINUM |
88 | Tailspin Toys (Dracut, MA) | PLATINUM |
89 | Tailspin Toys (Victory Gardens, NJ) | PLATINUM |
90 | Tailspin Toys (Tolna, ND) | PLATINUM |
91 | Tailspin Toys (Alstead, NH) | PLATINUM |
92 | Tailspin Toys (Sans Souci, SC) | PLATINUM |
93 | Tailspin Toys (Clewiston, FL) | PLATINUM |
94 | Tailspin Toys (Cheyenne Wells, CO) | PLATINUM |
95 | Tailspin Toys (Lavon, TX) | PLATINUM |
96 | Tailspin Toys (South La Paloma, TX) | PLATINUM |
97 | Tailspin Toys (Manchester Center, VT) | PLATINUM |
98 | Tailspin Toys (El Centro, CA) | PLATINUM |
99 | Tailspin Toys (Guin, AL) | PLATINUM |
Observe the query execution plan (or actual plan).
Notice the simplicity of the plan and estimated cost percentage for each operator. There is no cost estimated for the Compute Scalar because these are not realistically costed.
The iterative invocation nature of the UDF is also obfuscated from the plan:
Also note that the plan was executed serially. Scalar UDFs are parallelism inhibitors.
Run the same query from Step 3, but now without any hint, allowing SQL Server to operate as default.
USE [WideWorldImportersDW];
GO
ALTER DATABASE [WideWorldImportersDW] SET COMPATIBILITY_LEVEL = 150;
GO
ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;
GO
SELECT TOP 100 [Customer Key], [Customer],
dbo.ufn_customer_category([Customer Key]) AS [Discount Price]
FROM [Dimension].[Customer]
ORDER BY [Customer Key]
OPTION (RECOMPILE);
GO
Customer Key | Customer | Discount Price |
---|---|---|
0 | Unknown | PLATINUM |
1 | Tailspin Toys (Head Office) | PLATINUM |
2 | Tailspin Toys (Sylvanite, MT) | PLATINUM |
3 | Tailspin Toys (Peeples Valley, AZ) | PLATINUM |
4 | Tailspin Toys (Medicine Lodge, KS) | PLATINUM |
5 | Tailspin Toys (Gasport, NY) | PLATINUM |
6 | Tailspin Toys (Jessie, ND) | PLATINUM |
7 | Tailspin Toys (Frankewing, TN) | PLATINUM |
8 | Tailspin Toys (Bow Mar, CO) | PLATINUM |
9 | Tailspin Toys (Netcong, NJ) | PLATINUM |
10 | Tailspin Toys (Wimbledon, ND) | PLATINUM |
11 | Tailspin Toys (Devault, PA) | PLATINUM |
12 | Tailspin Toys (Biscay, MN) | PLATINUM |
13 | Tailspin Toys (Stonefort, IL) | PLATINUM |
14 | Tailspin Toys (Long Meadow, MD) | PLATINUM |
15 | Tailspin Toys (Batson, TX) | PLATINUM |
16 | Tailspin Toys (Coney Island, MO) | PLATINUM |
17 | Tailspin Toys (East Fultonham, OH) | PLATINUM |
18 | Tailspin Toys (Goffstown, NH) | PLATINUM |
19 | Tailspin Toys (Lemeta, AK) | PLATINUM |
20 | Tailspin Toys (College Place, WA) | PLATINUM |
21 | Tailspin Toys (Tresckow, PA) | PLATINUM |
22 | Tailspin Toys (Ward Ridge, FL) | PLATINUM |
23 | Tailspin Toys (Ikatan, AK) | PLATINUM |
24 | Tailspin Toys (Dundarrach, NC) | PLATINUM |
25 | Tailspin Toys (Avenal, CA) | PLATINUM |
26 | Tailspin Toys (Hedrick, IA) | PLATINUM |
27 | Tailspin Toys (Bowlus, MN) | PLATINUM |
28 | Tailspin Toys (North Ridge, NY) | PLATINUM |
29 | Tailspin Toys (Eulaton, AL) | PLATINUM |
30 | Tailspin Toys (Koontzville, WA) | PLATINUM |
31 | Tailspin Toys (Lake Stevens, WA) | PLATINUM |
32 | Tailspin Toys (Maypearl, TX) | PLATINUM |
33 | Tailspin Toys (Boyden Arbor, SC) | PLATINUM |
34 | Tailspin Toys (Dallardsville, TX) | PLATINUM |
35 | Tailspin Toys (Slanesville, WV) | PLATINUM |
36 | Tailspin Toys (Ekron, KY) | PLATINUM |
37 | Tailspin Toys (Kerby, OR) | PLATINUM |
38 | Tailspin Toys (Airport Drive, MO) | PLATINUM |
39 | Tailspin Toys (Diablock, KY) | PLATINUM |
40 | Tailspin Toys (Impact, TX) | PLATINUM |
41 | Tailspin Toys (Olivette, MO) | PLATINUM |
42 | Tailspin Toys (Arietta, NY) | PLATINUM |
43 | Tailspin Toys (Upper Preston, WA) | PLATINUM |
44 | Tailspin Toys (Amanda Park, WA) | PLATINUM |
45 | Tailspin Toys (Severna Park, MD) | PLATINUM |
46 | Tailspin Toys (Jemison, AL) | PLATINUM |
47 | Tailspin Toys (Lake Hughes, CA) | PLATINUM |
48 | Tailspin Toys (Trentwood, WA) | PLATINUM |
49 | Tailspin Toys (Muir, MI) | PLATINUM |
50 | Tailspin Toys (Hambleton, WV) | PLATINUM |
51 | Tailspin Toys (Imlaystown, NJ) | PLATINUM |
52 | Tailspin Toys (Idria, CA) | PLATINUM |
53 | Tailspin Toys (Nanafalia, AL) | PLATINUM |
54 | Tailspin Toys (Railroad, PA) | PLATINUM |
55 | Tailspin Toys (Gum Branch, GA) | PLATINUM |
56 | Tailspin Toys (Royal City, WA) | PLATINUM |
57 | Tailspin Toys (Lakemore, OH) | PLATINUM |
58 | Tailspin Toys (Mappsburg, VA) | PLATINUM |
59 | Tailspin Toys (Edgartown, MA) | PLATINUM |
60 | Tailspin Toys (East Dailey, WV) | PLATINUM |
61 | Tailspin Toys (Fairfield Glade, TN) | PLATINUM |
62 | Tailspin Toys (Great Neck Estates, NY) | PLATINUM |
63 | Tailspin Toys (Stallion Springs, CA) | PLATINUM |
64 | Tailspin Toys (Hodgdon, ME) | PLATINUM |
65 | Tailspin Toys (North Cowden, TX) | PLATINUM |
66 | Tailspin Toys (Madaket, MA) | PLATINUM |
67 | Tailspin Toys (Sentinel Butte, ND) | PLATINUM |
68 | Tailspin Toys (Gardere, LA) | PLATINUM |
69 | Tailspin Toys (Lytle Creek, CA) | PLATINUM |
70 | Tailspin Toys (New Baden, IL) | PLATINUM |
71 | Tailspin Toys (Good Hart, MI) | PLATINUM |
72 | Tailspin Toys (Cortaro, AZ) | PLATINUM |
73 | Tailspin Toys (Cokato, MN) | PLATINUM |
74 | Tailspin Toys (Indios, PR) | PLATINUM |
75 | Tailspin Toys (Windsor Locks, CT) | PLATINUM |
76 | Tailspin Toys (Yewed, OK) | PLATINUM |
77 | Tailspin Toys (Hahira, GA) | PLATINUM |
78 | Tailspin Toys (Sekiu, WA) | PLATINUM |
79 | Tailspin Toys (Page City, KS) | PLATINUM |
80 | Tailspin Toys (Valdese, NC) | PLATINUM |
81 | Tailspin Toys (Big Moose, NY) | PLATINUM |
82 | Tailspin Toys (La Cueva, NM) | PLATINUM |
83 | Tailspin Toys (Absecon, NJ) | PLATINUM |
84 | Tailspin Toys (Aceitunas, PR) | PLATINUM |
85 | Tailspin Toys (Andrix, CO) | PLATINUM |
86 | Tailspin Toys (New Lexington, OH) | PLATINUM |
87 | Tailspin Toys (Sauquoit, NY) | PLATINUM |
88 | Tailspin Toys (Dracut, MA) | PLATINUM |
89 | Tailspin Toys (Victory Gardens, NJ) | PLATINUM |
90 | Tailspin Toys (Tolna, ND) | PLATINUM |
91 | Tailspin Toys (Alstead, NH) | PLATINUM |
92 | Tailspin Toys (Sans Souci, SC) | PLATINUM |
93 | Tailspin Toys (Clewiston, FL) | PLATINUM |
94 | Tailspin Toys (Cheyenne Wells, CO) | PLATINUM |
95 | Tailspin Toys (Lavon, TX) | PLATINUM |
96 | Tailspin Toys (South La Paloma, TX) | PLATINUM |
97 | Tailspin Toys (Manchester Center, VT) | PLATINUM |
98 | Tailspin Toys (El Centro, CA) | PLATINUM |
99 | Tailspin Toys (Guin, AL) | PLATINUM |
Observe the query execution plan (or actual plan). Notice the plan now inlines the UDF logic, and SQL Server was able to optimize the plan adequately. As you can see from the execution times, the query with Batch Mode for Rowstore finished much faster! From ~9s to ~1s.
Notice the QueryTimeStats property of the SELECT: