Created
April 17, 2026 15:03
-
-
Save tcartwright/16070985fa6c091f75372238e4849506 to your computer and use it in GitHub Desktop.
SQL SERVER: Find plan cache queries using user defined functions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- Finds cached plans whose showplan XML contains a UDF reference, | |
| -- and tags them by DML statement type (SELECT/INSERT/UPDATE/DELETE/MERGE). | |
| WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p) | |
| SELECT | |
| udf_ref.value('@Database', 'sysname') AS udf_database, | |
| udf_ref.value('@Schema', 'sysname') AS udf_schema, | |
| udf_ref.value('@Table', 'sysname') AS udf_name, -- showplan (oddly) labels UDF names with @Table | |
| CASE | |
| WHEN UPPER(LTRIM(stmt_text)) LIKE 'INSERT%' THEN 'INSERT' | |
| WHEN UPPER(LTRIM(stmt_text)) LIKE 'UPDATE%' THEN 'UPDATE' | |
| WHEN UPPER(LTRIM(stmt_text)) LIKE 'DELETE%' THEN 'DELETE' | |
| WHEN UPPER(LTRIM(stmt_text)) LIKE 'MERGE%' THEN 'MERGE' | |
| WHEN UPPER(LTRIM(stmt_text)) LIKE 'SELECT%' THEN 'SELECT' | |
| WHEN UPPER(LTRIM(stmt_text)) LIKE 'WITH%' THEN 'SELECT (CTE)' | |
| ELSE 'OTHER' | |
| END AS statement_type, | |
| cp.objtype, | |
| DB_NAME(st.dbid) AS outer_database, | |
| OBJECT_SCHEMA_NAME(st.objectid, st.dbid) AS outer_object_schema, | |
| OBJECT_NAME(st.objectid, st.dbid) AS outer_object_name, | |
| cp.usecounts, | |
| qs.execution_count, | |
| qs.total_worker_time, | |
| qs.total_elapsed_time, | |
| qs.total_logical_reads, | |
| stmt_text AS statement_text, | |
| st.text AS full_batch_text, | |
| qp.query_plan | |
| FROM sys.dm_exec_cached_plans cp | |
| CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp | |
| CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st | |
| CROSS APPLY qp.query_plan.nodes('//p:UDF') AS u(udf_ref) | |
| OUTER APPLY ( | |
| SELECT TOP 1 qs.* | |
| FROM sys.dm_exec_query_stats qs | |
| WHERE qs.plan_handle = cp.plan_handle | |
| ORDER BY qs.execution_count DESC | |
| ) qs | |
| OUTER APPLY ( | |
| SELECT SUBSTRING( | |
| st.text, | |
| (ISNULL(qs.statement_start_offset, 0) / 2) + 1, | |
| (( CASE ISNULL(qs.statement_end_offset, -1) | |
| WHEN -1 THEN DATALENGTH(st.text) | |
| ELSE qs.statement_end_offset | |
| END - ISNULL(qs.statement_start_offset, 0)) / 2) + 1 | |
| ) AS stmt_text | |
| ) s | |
| WHERE qp.query_plan IS NOT NULL | |
| AND cp.objtype IN ('Adhoc', 'Prepared', 'Proc', 'Trigger') -- tighten as needed | |
| ORDER BY qs.execution_count DESC, cp.usecounts DESC; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment