You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -35,7 +35,7 @@ Scalar UDFs typically end up performing poorly due to the following reasons:
35
35
36
36
The goal of the scalar UDF inlining feature is to improve performance of queries that invoke T-SQL scalar UDFs, where UDF execution is the main bottleneck.
37
37
38
-
With this new feature, scalar UDFs are automatically transformed into scalar expressions or scalar subqueries that are substituted in the calling query in place of the UDF operator. These expressions and subqueries are then optimized. As a result, the query plan no longer has a user-defined function operator, but its effects are observed in the plan, like views or inline TVFs.
38
+
With this new feature, scalar UDFs are automatically transformed into scalar expressions or scalar subqueries that are substituted in the calling query in place of the UDF operator. These expressions and subqueries are then optimized. As a result, the query plan no longer has a user-defined function operator, but its effects are observed in the plan, like views or inline table-valued functions (TVFs).
39
39
40
40
## Examples
41
41
@@ -137,7 +137,7 @@ For the same query, the plan with the UDF inlined looks as follows.
137
137
138
138
As mentioned earlier, the query plan no longer has a user-defined function operator, but its effects are now observable in the plan, like views or inline TVFs. Here are some key observations from the previous plan:
139
139
140
-
-[!INCLUDE [ssNoVersion](../../includes/ssnoversion-md.md)] infers the implicit join between `CUSTOMER` and `ORDERS` and makes that explicit via a join operator.
140
+
-[!INCLUDE [ssNoVersion](../../includes/ssnoversion-md.md)] infers the implicit join between `CUSTOMER` and `ORDERS` and makes it explicit via a join operator.
141
141
142
142
-[!INCLUDE [ssNoVersion](../../includes/ssnoversion-md.md)] also infers the implicit `GROUP BY O_CUSTKEY on ORDERS` and uses the IndexSpool + StreamAggregate to implement it.
143
143
@@ -149,23 +149,23 @@ Depending upon the complexity of the logic in the UDF, the resulting query plan
149
149
150
150
## Inlineable scalar UDF requirements
151
151
152
-
A scalar T-SQL UDF can be inlined if the function definition uses allowed constructs AND the function is used in a context that enables inlining:
152
+
A scalar T-SQL UDF can be inlined if the function definition uses allowed constructs, and the function is used in a context that enables inlining:
153
153
154
-
Requirements of the UDF definition of the UDF, all must be true
154
+
All of the following conditions of the *UDF definition* must be true:
155
155
156
156
- The UDF is written using the following constructs:
157
157
-`DECLARE`, `SET`: Variable declaration and assignments.
158
158
-`SELECT`: SQL query with single/multiple variable assignments <sup>1</sup>.
159
159
-`IF`/`ELSE`: Branching with arbitrary levels of nesting.
160
160
-`RETURN`: Single or multiple return statements. Starting with [!INCLUDE [sql-server-2019](../../includes/sssql19-md.md)] CU5, the UDF can only contain a single RETURN statement to be considered for inlining <sup>6</sup>.
161
161
-`UDF`: Nested/recursive function calls <sup>2</sup>.
162
-
- Others: Relational operations such as `EXISTS`, `IS `NULL``.
162
+
- Others: Relational operations such as `EXISTS`, `IS NULL`.
163
163
- The UDF doesn't invoke any intrinsic function that is either time-dependent (such as `GETDATE()`) or has side effects <sup>3</sup> (such as `NEWSEQUENTIALID()`).
164
164
- The UDF uses the `EXECUTE AS CALLER` clause (default behavior if the `EXECUTE AS` clause isn't specified).
165
165
- The UDF doesn't reference table variables or table-valued parameters.
166
166
- The UDF isn't natively compiled (interop is supported).
167
167
- The UDF doesn't reference user-defined types.
168
-
- There are no signatures added to the UDF <sup>9</sup>.
168
+
- There are no signatures added to the UDF <sup>9</sup>.
169
169
- The UDF isn't a partition function.
170
170
- The UDF doesn't contain references to Common Table Expressions (CTEs).
171
171
- The UDF doesn't contain references to intrinsic functions that might alter the results when inlined (such as `@@ROWCOUNT`) <sup>4</sup>.
@@ -181,16 +181,6 @@ Requirements of the UDF definition of the UDF, all must be true
181
181
- The UDF doesn't contain references to `WITH XMLNAMESPACES` <sup>8</sup>.
182
182
- If the UDF definition runs into thousands of lines of code, [!INCLUDE [ssNoVersion](../../includes/ssnoversion-md.md)] might choose not to inline it.
183
183
184
-
Requirement of the execution context all must be true
185
-
- The UDF isn't used in `ORDER BY` clause.
186
-
- The query invoking a scalar UDF doesn't reference a scalar UDF call in its `GROUP BY` clause.
187
-
- The query invoking a scalar UDF in its select list with `DISTINCT` clause doesn't have an `ORDER BY` clause.
188
-
- The UDF isn't called from a RETURN statement <sup>6</sup>.
189
-
- The query invoking the UDF doesn't have Common Table Expressions (CTEs) <sup>8</sup>.
190
-
- The UDF-calling query doesn't use `GROUPING SETS`, `CUBE`, or `ROLLUP` <sup>7</sup>.
191
-
- The UDF-calling query doesn't contain a variable that is used as a UDF parameter for assignment (for example, `SELECT @y = 2`, `@x = UDF(@y)`) <sup>7</sup>.
192
-
- The UDF isn't used in a computed column or a check constraint definition.
193
-
194
184
<sup>1</sup> `SELECT` with variable accumulation/aggregation isn't supported for inlining (such as `SELECT @val += col1 FROM table1`).
195
185
196
186
<sup>2</sup> Recursive UDFs are inlined to a certain depth only.
@@ -207,19 +197,36 @@ Requirement of the execution context all must be true
207
197
208
198
<sup>8</sup> Restriction added in [!INCLUDE [sql-server-2019](../../includes/sssql19-md.md)] CU 11
209
199
210
-
<sup>9</sup> Because signatures could be added and dropped after a UDF is created, the decision whether to inline or not is done when the query referencing a scalar UDF is compiled. For example, system functions are typically signed with a certificate. You can use [sys.crypt_properties](../system-catalog-views/sys-crypt-properties-transact-sql.md) to find which objects are signed.
200
+
<sup>9</sup> Because signatures could be added and dropped after a UDF is created, the decision whether to inline is done when the query referencing a scalar UDF is compiled. For example, system functions are typically signed with a certificate. You can use [sys.crypt_properties](../system-catalog-views/sys-crypt-properties-transact-sql.md) to find which objects are signed.
201
+
202
+
All of the following requirement of the *execution context* must be true:
203
+
204
+
- The UDF isn't used in `ORDER BY` clause.
205
+
- The query invoking a scalar UDF doesn't reference a scalar UDF call in its `GROUP BY` clause.
206
+
- The query invoking a scalar UDF in its select list with `DISTINCT` clause doesn't have an `ORDER BY` clause.
207
+
- The UDF isn't called from a RETURN statement <sup>1</sup>.
208
+
- The query invoking the UDF doesn't have common table expressions (CTEs) <sup>3</sup>.
209
+
- The UDF-calling query doesn't use `GROUPING SETS`, `CUBE`, or `ROLLUP` <sup>2</sup>.
210
+
- The UDF-calling query doesn't contain a variable that is used as a UDF parameter for assignment (for example, `SELECT @y = 2`, `@x = UDF(@y)`) <sup>2</sup>.
211
+
- The UDF isn't used in a computed column or a check constraint definition.
212
+
213
+
<sup>1</sup> Restriction added in [!INCLUDE [sql-server-2019](../../includes/sssql19-md.md)] CU 5
214
+
215
+
<sup>2</sup> Restriction added in [!INCLUDE [sql-server-2019](../../includes/sssql19-md.md)] CU 6
216
+
217
+
<sup>3</sup> Restriction added in [!INCLUDE [sql-server-2019](../../includes/sssql19-md.md)] CU 11
211
218
212
219
For information on the latest T-SQL scalar UDF inlining fixes and changes to inlining eligibility scenarios, see the Knowledge Base article: [FIX: scalar UDF inlining issues in SQL Server 2019](https://support.microsoft.com/help/4538581).
213
220
214
-
### Check whether or not a UDF can be inlined
221
+
### Check whether a UDF can be inlined
215
222
216
-
For every T-SQL scalar UDF, the [sys.sql_modules](../system-catalog-views/sys-sql-modules-transact-sql.md) catalog view includes a property called `is_inlineable`, which indicates whether a UDF is inlineable or not.
223
+
For every T-SQL scalar UDF, the [sys.sql_modules](../system-catalog-views/sys-sql-modules-transact-sql.md) catalog view includes a property called `is_inlineable`, which indicates whether a UDF is inlineable.
217
224
218
225
The `is_inlineable` property is derived from the constructs found inside the UDF definition. It doesn't check whether the UDF is in fact inlineable at compile time. For more information, see the [conditions for inlining](#requirements).
219
226
220
-
A value of `1` indicates that it's inlineable, and `0` indicates otherwise. This property has a value of `1` for all inline TVFs as well. For all other modules, the value is `0`.
227
+
A value of `1` indicates that the UDF is inlineable, and `0` indicates otherwise. This property has a value of `1` for all inline TVFs as well. For all other modules, the value is `0`.
221
228
222
-
If a scalar UDF is inlineable, it doesn't imply that it's always inlined. [!INCLUDE [ssNoVersion](../../includes/ssnoversion-md.md)] decides (on a per-query, per-UDF basis) whether to inline a UDF or not. Check the lists of requirements above.
229
+
If a scalar UDF is inlineable, it doesn't imply that it's always inlined. [!INCLUDE [ssNoVersion](../../includes/ssnoversion-md.md)] decides (on a per-query, per-UDF basis) whether to inline a UDF. Refer to the lists of requirements earlier in this article.
223
230
224
231
```sql
225
232
SELECT*
@@ -228,12 +235,12 @@ If a scalar UDF is inlineable, it doesn't imply that it's always inlined. [!INCL
228
235
ONcp.major_id=o.object_id;
229
236
```
230
237
231
-
### Check whether inlining has happened or not
238
+
### Check whether inlining has happened
232
239
233
-
If all the preconditions are satisfied and [!INCLUDE [ssNoVersion](../../includes/ssnoversion-md.md)] decides to perform inlining, it transforms the UDF into a relational expression. From the query plan, it's easy to figure out whether inlining has occurred:
240
+
If all the preconditions are satisfied and [!INCLUDE [ssNoVersion](../../includes/ssnoversion-md.md)] decides to perform inlining, it transforms the UDF into a relational expression. From the query plan, you can figure out whether inlining occurred:
234
241
235
242
- The plan XML doesn't have a `<UserDefinedFunction>` XML node for a UDF that is inlined successfully.
236
-
- Certain XEvents are emitted.
243
+
- Certain Extended Events are emitted.
237
244
238
245
## Enable scalar UDF inlining
239
246
@@ -333,7 +340,7 @@ As described in this article, scalar UDF inlining transforms a query with scalar
333
340
334
341
- If a UDF references built-in functions such as `SCOPE_IDENTITY()`, `@@ROWCOUNT`, or `@@ERROR`, the value returned by the built-in function changes with inlining. This change in behavior is because inlining changes the scope of statements inside the UDF. Starting with [!INCLUDE [sql-server-2019](../../includes/sssql19-md.md)] CU2, inlining is blocked if the UDF references certain intrinsic functions (for example `@@ROWCOUNT`).
335
342
336
-
- If a variable is assigned with the result of an inlined UDF and it also used as index_column_name in FORCESEEK [Query hints](../../t-sql/queries/hints-transact-sql-query.md), it results in error Msg 8622 indicating that the Query processor couldn't produce a query plan because of the hints defined in the query.
343
+
- If a variable is assigned with the result of an inlined UDF and it also used as `index_column_name` in `FORCESEEK`[Query hints](../../t-sql/queries/hints-transact-sql-query.md), it results in error 8622, indicating that the query processor couldn't produce a query plan because of the hints defined in the query.
0 commit comments