6

I have a query that is both prone to parameter sensitivity and is suffering from its table-valued parameter. I'm lazy and just want to solve this with query hints. When I'm lazy, I can solve parameter sensitivity with OPTION (OPTIMIZE FOR UNKNOWN). This gives me three very tightly related questions:

  1. Does OPTION (OPTIMIZE FOR UNKNOWN) have any impact on table-valued variables?
  2. Does OPTION (OPTIMIZE FOR UNKNOWN) have any impact on table-valued parameters?
  3. What query hints other than OPTION(RECOMPILE) can be used to influence cardinality estimates for table-valued parameters?

Both my own experiments and the documentation suggest that this is a lost cause. Table-valued parameters are subject to parameter sniffing, but all that gets sniffed is there cardinality so OPTION (OPTIMIZE FOR UNKNOWN) ought to do nothing.

As for the question of using other table hints, I know that I can change the estimate for operations on the table-valued parameter (e.g. OPTION (USE HINT ('ASSUME_MIN_SELECTIVITY_FOR_FILTER_ESTIMATES')) but I do not think that hints can influence the cardinality estimate for the parameter itself.

1 Answer 1

7

oops

It does seem that OPTIMIZE FOR UNKNOWN has no effect at all on either table variables or parameters. In fact, if you try to specify OPTIMIZE FOR (@data UNKNOWN) or OPTIMIZE FOR (@data = NULL) then you get

Msg 137 Level 16 State 1 Line 7
Must declare the scalar variable "@data".

which tells us this hint is only intended for scalar variables, entirely undocumented.

Historically, table variables did not get any cardinality estimation and had an estimate of 1 or 100 rows. Table Variable Deferred Compilation was introduced using trace flag 2453, and from 2019 as a database which you can disable using a hint. This will usually cause the estimates to be correct, which in your case seems to be undesired.


get the hint

  • Turning off TF2453, or using the query hint USE HINT (N'DISABLE_DEFERRED_COMPILATION_TV') only works on table variables, not parameters.
  • ROBUST PLAN and FAST 1 do not affect cardinality estimation here.

the bad

You can insert the whole thing into a table variable. This by default gets an estimate of 1 or 100 rows, depending on version.

You also need to turn off TF2453 if it's on. On 2019 and later, you need to instead disable Deferred Table Variable Compilation. You could instead use QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_100, but that's a broad hammer.

CREATE OR ALTER PROCEDURE p
  @data dbo.t READONLY
AS

DECLARE @data2 dbo.t;
INSERT @data2
SELECT *
FROM @data;

SELECT COUNT(*) AS c
FROM @data2
OPTION (USE HINT (N'DISABLE_DEFERRED_COMPILATION_TV'));

This has the obvious downside of needing to copy the entire TVP, which may be very large.


the ugly

You can use sp_prepare and sp_execute with dynamic SQL, to force compilation without cardinality estimation, as noted in @PaulWhite's article. There is no copying in this instance.

CREATE OR ALTER PROCEDURE p
  @data dbo.t READONLY
AS

DECLARE @handle int;
EXEC sp_prepare @handle OUT,
  N'@data dbo.t READONLY',
  N'
SELECT COUNT(*) AS c
FROM @data;
';
EXEC sp_execute @handle, @data = @data;

This only works with simple statements, otherwise compilation gets deferred and the parameter will be sniffed.

But it makes the procedure a bit of a mess to write and debug.


the good

Just sort your query out. Why does OPTION (RECOMPILE) not solve the issue, or what is throwing off the optimizer when it does have the correct estimate? Solve that instead.

Or just get a good plan and force it using Query Store.


You can see the options in this fiddle.

2
  • Just for the sake of ultimate clarity, are there any query hints other than OPTION(RECOMPILE) that can be used to influence cardinality estimates for table-valued parameters? TF2453 and its friends are just for variables.
    – J. Mini
    Commented 13 hours ago
  • Not that I know of, that's what I was trying to say at the beginning Commented 12 hours ago

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.