Out of #466, where @joshdbe copied a statement out of a plan and got @0, @1 … @6 where his literals had been.
That text is correct — it is what SQL Server compiled, because the database has PARAMETERIZATION FORCED — but it is not what anyone wants on the clipboard. You copy a statement out of a plan to run it, and a query full of undeclared @0 will not run.
The values are right there
Forced parameterization keeps every literal in the plan's ParameterList, with both compiled and runtime values. From a local repro of #466:
<ColumnReference Column="@0" ParameterDataType="varchar(8000)" ParameterCompiledValue="'123456'" ParameterRuntimeValue="'123456'" />
<ColumnReference Column="@1" ParameterDataType="int" ParameterCompiledValue="(5)" ParameterRuntimeValue="(5)" />
...
<ColumnReference Column="@6" ParameterDataType="varchar(8000)" ParameterCompiledValue="'2026-05-28 10:28:07.3132561'" />
ShowParameters already parses and displays all of this. Nothing new needs reading — the substitution just is not offered anywhere.
What to add
On the statement context menu, alongside the existing Copy Query Text and Open in Query Editor, a variant that puts the parameter values back:
- Substitute
ParameterRuntimeValue where present, falling back to ParameterCompiledValue. Values arrive pre-quoted ('123456', (5)) — the parens on numerics need stripping, the quotes on strings do not.
- Match on whole tokens.
@1 must not match inside @11, and a @0 inside a string literal must be left alone.
- Only offer it when the statement actually has parameters. A statement with none should not grow a dead menu item.
- Worth considering whether this should be the default behavior of the existing two items rather than a third and fourth item, with the raw form as the variant. The parameterized text is the honest record of what was compiled, but the substituted text is what people want to run, and a context menu with four near-identical entries is its own problem.
This is equally useful for procedure parameters and sp_executesql, not just forced parameterization — any plan whose statement text carries @name and whose ParameterList carries the value.
Note for whoever picks this up
Do not reach for the query editor's buffer as the source of truth. It only holds the original text in the one case where the user just executed it from that tab, and it is wrong for a plan opened from a file, from Query Store, or from another session.
Out of #466, where @joshdbe copied a statement out of a plan and got
@0,@1…@6where his literals had been.That text is correct — it is what SQL Server compiled, because the database has
PARAMETERIZATION FORCED— but it is not what anyone wants on the clipboard. You copy a statement out of a plan to run it, and a query full of undeclared@0will not run.The values are right there
Forced parameterization keeps every literal in the plan's
ParameterList, with both compiled and runtime values. From a local repro of #466:ShowParametersalready parses and displays all of this. Nothing new needs reading — the substitution just is not offered anywhere.What to add
On the statement context menu, alongside the existing Copy Query Text and Open in Query Editor, a variant that puts the parameter values back:
ParameterRuntimeValuewhere present, falling back toParameterCompiledValue. Values arrive pre-quoted ('123456',(5)) — the parens on numerics need stripping, the quotes on strings do not.@1must not match inside@11, and a@0inside a string literal must be left alone.This is equally useful for procedure parameters and
sp_executesql, not just forced parameterization — any plan whose statement text carries@nameand whose ParameterList carries the value.Note for whoever picks this up
Do not reach for the query editor's buffer as the source of truth. It only holds the original text in the one case where the user just executed it from that tab, and it is wrong for a plan opened from a file, from Query Store, or from another session.