Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Query times out in .Net SqlCommand.ExecuteNonQuery, works in SQL Server Management Studio

Update: Problem solved, and staying solved. If you want to see the site in action, visit Tweet08

I've got several queries that act differently in SSMS versus when run inside my .Net application. The SSMS executes fine in under a second. The .Net call times out after 120 seconds (connection default timeout).

I did a SQL Trace (and collected everything) I've seen that the connection options are the same (and match the SQL Server's defaults). The SHOWPLAN All, however, show a huge difference in the row estimates and thus the working version does an aggressive Table Spool, where-as the failing call does not.

In the SSMS, the datatypes of the temp variables are based on the generated SQL Parameters in the .Net, so they are the same.

The failure executes under Cassini in a VS2008 debug session. The success is under SSMS 2008 . Both are running against the same destination server form the same network on the same machine.

Query in SSMS:

DECLARE @ContentTableID0 TINYINT
DECLARE @EntryTag1 INT
DECLARE @ContentTableID2 TINYINT
DECLARE @FieldCheckId3 INT
DECLARE @FieldCheckValue3 VARCHAR(128)
DECLARE @FieldCheckId5 INT
DECLARE @FieldCheckValue5 VARCHAR(128)
DECLARE @FieldCheckId7 INT 
DECLARE @FieldCheckValue7 VARCHAR(128)
SET @ContentTableID0= 3
SET @EntryTag1= 8
SET @ContentTableID2= 2
SET @FieldCheckId3= 14
SET @FieldCheckValue3= 'igor'
SET @FieldCheckId5= 33
SET @FieldCheckValue5= 'a'
SET @FieldCheckId7= 34
SET @FieldCheckValue7= 'a'

SELECT COUNT_BIG(*)
FROM dbo.ContentEntry AS mainCE
WHERE GetUTCDate() BETWEEN mainCE.CreatedOn AND mainCE.ExpiredOn
AND (mainCE.ContentTableID=@ContentTableID0)
AND ( EXISTS (SELECT *
              FROM dbo.ContentEntryLabel
              WHERE ContentEntryID = mainCE.ID
              AND GetUTCDate() BETWEEN CreatedOn AND ExpiredOn
              AND LabelFacetID = @EntryTag1))
      AND (mainCE.OwnerGUID IN (SELECT TOP 1 Name
                                FROM dbo.ContentEntry AS innerCE1
                                WHERE GetUTCDate() BETWEEN innerCE1.CreatedOn AND innerCE1.ExpiredOn
                                AND (innerCE1.ContentTableID=@ContentTableID2
                                     AND EXISTS (SELECT *
                                                 FROM dbo.ContentEntryField
                                                 WHERE ContentEntryID = innerCE1.ID
                                                 AND (ContentTableFieldID = @FieldCheckId3
                                                      AND DictionaryValueID IN (SELECT dv.ID
                                                                                FROM dbo.DictionaryValue AS dv
                                                                                WHERE dv.Word LIKE '%' + @FieldCheckValue3 + '%'))
                                                )
                                    )
                               )
           OR EXISTS (SELECT *
                      FROM dbo.ContentEntryField
                      WHERE ContentEntryID = mainCE.ID
                      AND (   (ContentTableFieldID = @FieldCheckId5
                               AND DictionaryValueID IN (SELECT dv.ID
                                                         FROM dbo.DictionaryValue AS dv
                                                         WHERE dv.Word LIKE '%' + @FieldCheckValue5 + '%')
                              )
                           OR (ContentTableFieldID = @FieldCheckId7
                               AND DictionaryValueID IN (SELECT dv.ID
                                                         FROM dbo.DictionaryValue AS dv
                                                         WHERE dv.Word LIKE '%' + @FieldCheckValue7 + '%')
                               )
                          )
                     )
          )

Trace's version of .Net call (some formatting added):

exec sp_executesql N'SELECT COUNT_BIG(*) ...'
,N'@ContentTableID0 tinyint
,@EntryTag1 int
,@ContentTableID2 tinyint
,@FieldCheckId3 int
,@FieldCheckValue3 varchar(128)
,@FieldCheckId5 int
,@FieldCheckValue5 varchar(128)
,@FieldCheckId7 int
,@FieldCheckValue7 varchar(128)'
,@ContentTableID0=3
,@EntryTag1=8
,@ContentTableID2=2
,@FieldCheckId3=14
,@FieldCheckValue3='igor'
,@FieldCheckId5=33
,@FieldCheckValue5='a'
,@FieldCheckId7=34
,@FieldCheckValue7='a'
like image 993
IDisposable Avatar asked Jun 11 '09 20:06

IDisposable


2 Answers

It is not your indexes.

This is parameter-sniffing, as it usually happens to parametrized stored procedures. It is not widely known, even among those who know about parameter-sniffing, but it can also happen when you use parameters through sp_executesql.

You will note that the version that you are testing in SSMS and the version the the profiler is showing are not identical because the profiler version shows that your .Net application is executing it through sp_executesql. If you extract and execute the full sql text that is actually being run for your application, then I believe that you will see the same performance problem with the same query plan.

FYI: the query plans being different is the key indicator of parameter-sniffing.

FIX: The easiest way to fix this one assuming it is executing on SQL Server 2005 or 2008 is to add the clause "OPTION (RECOMPILE)" as the last line of you SELECT statement. Be forewarned, you may have to execute it twice before it works and it does not always work on SQL Server 2005. If that happens, then there are other steps that you can take, but they are a little bit more involved.

One thing that you could try is to check and see if "Forced Parameterization" has been turned on for your database (it should be in the SSMS Database properties, under the Options page). To tunr Forced Parameterization off execute this command:

    ALTER DATABASE [yourDB] SET  PARAMETERIZATION SIMPLE 
like image 58
RBarryYoung Avatar answered Sep 27 '22 19:09

RBarryYoung


I ran into this situation today and the fix that solved my problem is to use WITH (NOLOCK) while doing a select on tables:

Eg: If your stored proc has T-SQL that looks like below:

SELECT * FROM [dbo].[Employee]

Change it to

SELECT * FROM [dbo].[Employee] WITH (NOLOCK)

Hope this helps.

like image 40
Kiran Banda Avatar answered Sep 27 '22 17:09

Kiran Banda