0

I have a stored procedure like this:

CREATE PROCEDURE [dbo].[GetInventoryData] 
    @supplierId UNIQUEIDENTIFIER,
    @numbers dbo.ListNumbers READONLY,
    @locations dbo.ListLocations READONLY
AS
BEGIN
    SET NOCOUNT ON; 
    
    SELECT DISTINCT
        i.Field1,
        i.Field2,
        i.Field3,
        i.Field4
    FROM 
        dbo.Inventory AS i WITH (index(idx_Inventory_Abc_Xyz))
    JOIN 
        @numbers o ON i.OemNumber = o.OemNumber
    JOIN 
        @locations AS l ON l.YardLocation = i.YardLocation
    WHERE 
        i.SupplierId = @supplierId
        AND i.PartType <> 'ABC'
        AND i.PartType <> 'XYZ'
END

This is how I call the stored procedure:

DECLARE @p2 dbo.Locations  

INSERT INTO @p2 VALUES (N'AA1')  
INSERT INTO @p2 VALUES (N'AA3')    

DECLARE @p3 dbo.ListNumbers  

INSERT INTO @p3 VALUES (N'631006CA0A')    


EXEC GetInventoryData 
           @supplierId = 'e418fac4-c89e-4f5d-ad7d-ee7fcba7f41f',
           @locations = @p2,
           @numbers = @p3 

The above stored procedure sometime got timeout while almost time it just took < 1s.

I check system and see that compilations/sec is high, and it suggested that ad-hoc queries can be reason.

Then I used this query to list ad-hoc query:

SELECT text, cp.objtype, cp.size_in_bytes
FROM sys.dm_exec_cached_plans AS cp 
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st
WHERE cp.cacheobjtype = N'Compiled Plan'
  AND cp.objtype IN (N'Adhoc', N'Prepared')
  AND cp.usecounts = 1
ORDER BY cp.size_in_bytes DESC 
OPTION (RECOMPILE);

!!! Here is sql plan: https://www.brentozar.com/pastetheplan/?id=BkP5cAOW9

My question is why my stored procedure an ad-hoc query? I guess table value parameter cause it. Can someone explain, also give me some idea to fix issue please

Update: added .NET code to call stored procedure

enter image description here

Thank you

19
  • 2
    Why did you need to use index hint for idx_Inventory_Abc_Xyz index? Commented Mar 10, 2022 at 2:19
  • 1
    Its pretty rare that you can pick a better index than SQL Server. Commented Mar 10, 2022 at 2:23
  • 1
    The thing about investigating performance issues is to simplify the factors as much as possible... Commented Mar 10, 2022 at 2:31
  • 1
    For heavy use of differently sized Table Valued Parameters it might be worth switching on Trace Flag 2453, see brentozar.com/archive/2017/02/…. If you are calling the procedure from a client app then do not use the batch at the top (with the DECLARE... INSERT) instead use the client driver's tools to pass a Table Valued Parameter directly Commented Mar 10, 2022 at 12:22
  • 1
    You will find that it doesn't actually generate that code, it is merely a represntation generated by the SQL Profiler of what the inserts might look like if they were T-SQL code. It's actually a high-performance BULK INSERT style codepath sent directly over the TDS protocol, and the recompilation count is actually a artifact that can be safely ignored. See also sqlskills.com/blogs/bobb/tvps-and-plan-compilation-the-reprise and stackoverflow.com/a/56744520/14868997 Commented Mar 10, 2022 at 13:48

1 Answer 1

1

The above stored procedure sometime got timeout while almost time it just took <1s.

Because you might encounter parameter sniffing,

values passed into the parameter are evaluated and used to create an execution plan for stored procedure that stored execution plan in the plan cache.

because compiling queries is expensive, SQL Server will re-use them as much as possible.

That will cause the execution plan might not be the best for every query parameters.

There are some ways you can avoid parameter sniffing

  1. Add OPTION(RECOMPILE) in the queries or procedure

that way will tell sql-server don't try to keep the execution plan, so you will get a suitable plan from QO every execution time.

CREATE PROCEDURE [dbo].[GetInventoryData] 
    @supplierId UNIQUEIDENTIFIER,
    @numbers dbo.ListNumbers READONLY,
    @locations dbo.ListLocations READONLY
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON; 
    
    SELECT DISTINCT
        i.Field1,
        i.Field2,
        i.Field3,
        i.Field4
    FROM dbo.Inventory AS i with(index(idx_Inventory_Abc_Xyz))
    JOIN @numbers o ON i.OemNumber = o.OemNumber
    JOIN @locations AS l ON l.YardLocation = i.YardLocation
    WHERE i.SupplierId = @supplierId
    AND i.PartType <> 'ABC'
    AND i.PartType <> 'XYZ'
    OPTION(RECOMPILE)
END

But this way might cause your CPU high, if you execute stored procedure many times in short time

  1. Add OPTION (OPTIMIZE FOR UNKNOWN) or DECLARE local var to carry parameter.

OPTIMIZE FOR UNKNOWN That will create a plan that it expects to work well for all values of all parameters.

CREATE PROCEDURE [dbo].[GetInventoryData] 
    @supplierId UNIQUEIDENTIFIER,
    @numbers dbo.ListNumbers READONLY,
    @locations dbo.ListLocations READONLY
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON; 
    
    SELECT DISTINCT
        i.Field1,
        i.Field2,
        i.Field3,
        i.Field4
    FROM dbo.Inventory AS i with(index(idx_Inventory_Abc_Xyz))
    JOIN @numbers o ON i.OemNumber = o.OemNumber
    JOIN @locations AS l ON l.YardLocation = i.YardLocation
    WHERE i.SupplierId = @supplierId
    AND i.PartType <> 'ABC'
    AND i.PartType <> 'XYZ'
    OPTION (OPTIMIZE FOR UNKNOWN)
END

parameter-sniffing-in-sql-server

Sign up to request clarification or add additional context in comments.

4 Comments

I actually tried OPTION(RECOMPILE) long time ago, I also split stored proc to small stored proceduces to avoid parameter sniffing. My stored proc is called many many times all of day. Base on posts: brentozar.com/askbrent/compilations and sqlskills.com/blogs/bobb/tvps-and-plan-compilation-the-reprise ..... I think it's mechanism of sql server. I'm trying to turn on "optimize for ad hoc workloads" btw.
one more note, I also tried to using brentozar scripts github.com/BrentOzarULTD/SQL-Server-First-Responder-Kit but don't see it warning "parameter sniffing"
If your SQL server support QueyStore I would recommend you use that to analytics. execute plan from your SP learn.microsoft.com/en-us/sql/relational-databases/performance/…
If not you can try to use who is active to collect what's different between slow query and normal query plan, we can discuss it by those information whoisactive.com

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.