Module Execution Statistics
Overview
SPs by Logical Writes covers one measure for stored procedures only, and nothing else in the product read sys.dm_exec_trigger_stats, so a trigger that costs a second on every insert was hard to find. The Module Execution Statistics report answers which code in this database costs the most? for every kind of module the plan cache keeps statistics for:
- stored procedures from
sys.dm_exec_procedure_stats, - triggers from
sys.dm_exec_trigger_stats, with the table they are on and the events they fire for, - scalar functions from
sys.dm_exec_function_stats(SQL Server 2016 and later).
Each module is ranked by the measure you pick, with totals, averages per call, per minute rates since the plan was cached, and its share of the database’s total.
A second page, Module Execution Statistics by Database, reads every database on the instance.
Where to find it
| Route | How |
|---|---|
| Database tree | Expand a database → Real Time → Module Execution Statistics |
| Related Links bar | From Tables With Triggers, Scalar UDF Inlining and Plan Guides and Hints |
| Module Execution Statistics by Database | Right-click a row → Open Module Execution Statistics for <database> |
The page is offered on SQL Server 2008 and later. The page title reads Module Execution Statistics for <database name>.
The toolbar
| Control | What it does |
|---|---|
| Refresh | Reads the plan cache again |
| CPU / Duration / Reads / Writes / Phys Reads / Executions / Avg CPU / Avg Duration | The measure the chart draws, the grid is sorted by and the percent column follows. CPU by default |
| All / Procedures / Triggers / Functions | Which kind of module the chart and grid show. All by default |
| Cancel | Stops a read in progress (shown only while reading) |
The measure and the type filter are remembered for next time. Changing them does not read the server again.
Reading the page
The heading of the chart says what is drawn, for example Top modules by CPU in Sales, and how many procedures, triggers and functions are cached. The note under it says when the oldest plan on the page was cached and how long ago, because every figure here resets when a plan leaves the cache, followed by anything that could not be read.
The chart draws the top 20 modules for the measure as horizontal bars, colored by type: blue for a procedure, orange for a trigger, green for a scalar function. Modules with nothing recorded for the measure are left out. Hover a bar for the executions, the CPU and duration in total and per call, the logical reads and writes, and when the plan was cached; click it to select the row in the grid.
The grid lists every module that passes the type filter, largest first by the measure:
| Column | What it is |
|---|---|
| Module | schema.name, or the object id when the name cannot be read |
| Type | Procedure, Trigger or Function |
| Parent Table (Events) | For a trigger, the table it is on (or Database for a database DDL trigger) with its events, INSTEAD OF and disabled. A disabled trigger’s entry is gray |
| Executions | execution_count |
| CPU ms, Avg CPU ms | total_worker_time and the average per call |
| Duration ms, Avg Duration ms, Max Duration ms | total_elapsed_time, the average per call and the longest single call |
| Logical Reads, Avg Reads | Total pages read and the average per call |
| Logical Writes | Total pages written |
| Physical Reads | Pages read from disk |
| Spills | total_spills; empty on builds without that column (before SQL Server 2016 SP2 and 2017) |
| Execs per Minute, CPU ms per Minute, Reads per Minute | The total divided by the whole minutes since the plan was cached, never less than one |
| % of Database <measure> | The module’s share of the database total for the measure, whatever the type filter. For Avg CPU and Avg Duration it is the share of total CPU and total duration |
| Cached Since | When the plan was cached |
| Last Execution | When it last ran |
| Plans | How many cached plans were added together |
A module compiled under two sets of SET options, or recompiled while an old plan was still running, has more than one cached plan. The report adds them into one row: the totals and counts are summed, the longest call is kept, Cached Since is the earliest of them and Last Execution the latest.
Hover a row for the same summary the chart shows. Selecting a row outlines its bar.
Right-click actions
| Item | What it does |
|---|---|
| Show Cached Plan (or double-click) | Opens the module’s most executed cached plan in the plan analyzer. If the plan has left the cache since, the page says so |
| Script Module Definition | Shows the module’s definition in a query window, with a USE for its database. Nothing is run. An encrypted or CLR module, one dropped since, or one your login cannot see has no definition to show |
| Open Tables With Triggers for <database> | On a trigger |
| Open Scalar UDF Inlining for <database> | On a function, on SQL Server 2019 and later |
| Copy the top modules as text | The heading, the note and the top ten modules for the measure |
| Go to | Schema Search for the module and, for a trigger on a table, the table pages |
Right-click the chart to copy it as an image. The grid exports to CSV and Excel like every other grid.
Permissions and versions
- The report reads
sys.dm_exec_procedure_stats,sys.dm_exec_trigger_statsandsys.dm_exec_function_stats, which need VIEW SERVER STATE (VIEW SERVER PERFORMANCE STATE on SQL Server 2022 and later). Each view is read on its own, so a refused view costs only its part and the note says which. If every view is refused, the page says the statistics could not be read and names the permission. - Scalar function statistics need SQL Server 2016 or later. On an older instance the note says only procedures and triggers are shown.
- A trigger’s parent table and events come from
sys.triggersandsys.trigger_eventsin the database. If they cannot be read, the parent column shows (unknown) and the note says why. - The statistics are cumulative only since each plan was cached, and are lost when a plan leaves the cache, on a restart or after
DBCC FREEPROCCACHE. A module with no plan in the cache is not listed. - The read runs in the background with a 120 second timeout, and can be canceled.