Skip to content

sp_QuickieStore: added sys.dm_db_tuning_recommendations to Expert Mode output#691

Merged
erikdarlingdata merged 1 commit intoerikdarlingdata:devfrom
ReeceGoding:try-dm_db_tuning_recommendations
Mar 3, 2026
Merged

sp_QuickieStore: added sys.dm_db_tuning_recommendations to Expert Mode output#691
erikdarlingdata merged 1 commit intoerikdarlingdata:devfrom
ReeceGoding:try-dm_db_tuning_recommendations

Conversation

@ReeceGoding
Copy link
Copy Markdown
Contributor

In hopes of closing #483. You will notice that I have copy and pasted Erik's JSON-shredding code from there.

Nothing truly intelligent done here. Erik has put a year and a half's more thought into this than me, so he will have better ideas for what data to include and what column name(s) to use. All that I have tried to do is make sp_QuickieStore get something useful from sys.dm_db_tuning_recommendations. All of this is locked behind @Expert_Mode = 1.

My only addition is the plan_force_recommendation_status column in the main sp_QuickieStore output. It is shown in the screenshot below. To add context, the screenshot also shows part of a row from sys.dm_db_tuning_recommendations, as well as the JSON. Neither of these have been imported into sp_QuickieStore. All that I did was add the plan_force_recommendation_status column. Getting a good screenshot of these wide tables is so hard that I might have to turn to PowerShell next time.

quickerik

For testing, I suggest copying this demo of Kendra Little's. Getting automatic tuning to kick in without help is hard.

I am not aware of any surprising cases that we need to defend against. Every column I have used has been from best guesses derived from playing with demo scripts. I am ignorant of the kinds of lessons that could only be learned from giving the feature a year of serious use. The only person I know of who has that experience would be Kendra Little. Incidentally, one of her main complaints about automatic tuning is that sys.dm_db_tuning_recommendations is not persisted. If Erik's new monitoring tool persists Expert Mode data, then this would solve one of Kendra's complaints. The other was regarding sys.sp_configure_automatic_tuning, which has already been fixed.

@ReeceGoding ReeceGoding changed the base branch from main to dev March 2, 2026 22:17
Copy link
Copy Markdown
Owner

@erikdarlingdata erikdarlingdata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Nice addition — surfacing sys.dm_db_tuning_recommendations in Expert Mode is a good call, and locking it behind @expert_mode = 1 is the right scope.

Three findings:


Bug: Status message text is backwards

In the OUTER APPLY, the plan_force_recommendation_status message says:

"Considered regressed from plan_id [recommended_plan_id]"

But recommended_plan_id is the better plan, not the regressed one. As written, it reads like the recommended plan is the bad one. Consider something like:

plan_force_recommendation_status =
    N'Recommended plan_id ' +
    CONVERT(nvarchar(20), qstr.recommended_plan_id) +
    N' is scored ' +
    CONVERT(nvarchar(20), qstr.score) +
    N' out of 100 for being better than this plan.'

Concern: SUBSTRING parsing throws on missing trailing comma

The CHARINDEX(',', ...) calls assume there's always a comma after regressedPlanId and recommendedPlanId. If either field is the last value in the flattened string (no trailing comma), CHARINDEX returns 0 and the SUBSTRING length goes negative:

-- Msg 536: Invalid length parameter passed to the SUBSTRING function.

With the current known JSON structure from Microsoft, both fields always have subsequent fields, so this works today. The risk is forward-compatibility if Microsoft changes the field ordering or removes fields in a future version. A fallback to LEN(detail) when CHARINDEX returns 0 would make it bulletproof.


Concern: OUTER APPLY runs unnecessarily without @expert_mode guard

The data collection into #tuning_recommendations is gated by @new = 1 AND @expert_mode = 1, but the OUTER APPLY is only inside the @new = 1 block. When @expert_mode = 0, the temp table is empty and the join is wasted work.

Fix: pull the OUTER APPLY out into its own block, same pattern as the wait stats CROSS APPLY above it:

    ) AS w'
    );
    END; /*End wait stats query*/

    IF
    (
        @new = 1
    AND @expert_mode = 1
    )
    BEGIN
        SELECT
            @sql +=
        CONVERT
        (
            nvarchar(max),
            N'
    OUTER APPLY
    (
        SELECT TOP (1)
            plan_force_recommendation_status =
                ...
        FROM #tuning_recommendations AS qstr
        WHERE qstr.regressed_plan_id = qsrs.plan_id
        AND   qstr.database_id = qsrs.database_id
        ORDER BY
            qstr.last_refresh DESC
    ) AS tr'
        );
    END; /*End tuning recommendations query*/

    /*Strap on the query hash totals table*/

This keeps it as its own independent block rather than nesting inside the wait stats @new = 1 check.

@ReeceGoding
Copy link
Copy Markdown
Contributor Author

ReeceGoding commented Mar 3, 2026

On the points:

  1. I think that the review bot doesn't know what "regressed from" means. I don't like saying that the other plan_id is "recommended" because it isn't really; it's just considered better than the regressed plan. I'm happy to take suggestions?
  2. The SUBSTRING is Erik's code. He is much more qualified to debug it than I.
  3. I don't really care about point 3. I can fix it if Erik wants?

@erikdarlingdata erikdarlingdata merged commit 80cece7 into erikdarlingdata:dev Mar 3, 2026
5 of 6 checks passed
erikdarlingdata added a commit that referenced this pull request Mar 3, 2026
…items

@find_high_impact = 1 identifies the vital few queries consuming
disproportionate resources. Scores queries across 6 dimensions (CPU,
duration, reads, writes, memory, executions) using PERCENT_RANK,
shows share-of-total per metric, and surfaces diagnostic signals
(parameter sensitivity, plan instability, spills, wait time).
Tested on SQL Server 2016-2025.

Also fixes two items from Reece's tuning recommendations PR (#691):
- SUBSTRING parsing: added IIF fallback when CHARINDEX returns 0
  so field reordering in future SQL Server versions won't throw
  Msg 536 (invalid length parameter)
- OUTER APPLY for plan_force_recommendation_status: pulled into its
  own @new = 1 AND @expert_mode = 1 block so it doesn't run as
  wasted work when expert mode is off

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants