views:

57

answers:

1

I have a L2S query with several joins that should returns 11 records in about 3 seconds. However, it times out after 30 seconds unless I specify a Take parameter (i used Take(20) even though it only returns 11 records) in which case it returns in the expected time frame of 3 seconds with the 11 records.

The query looks like this:

(from q in TransmittalDetails where q.TransmittalHeader.TransmittalEntityID == 196
      && q.TransmittalHeader.DateRangeBeginTimeID == 20100101
      && q.TransmittalHeader.ScenarioID == 2
      && q.LineItem.AccountType.AccountCategory.AccountGroup.
             AccountSummary.AccountSummaryID == 6
 select new {
q.LineItem.AccountType.AccountCategory.AccountGroup.AccountGroupID,
q.LineItem.AccountType.AccountCategory.AccountGroup.AccountGroup1
   }).Distinct()

This generates some SQL that looks like this:

DECLARE @p0 Int = 196
DECLARE @p1 Int = 20100101
DECLARE @p2 Int = 2
DECLARE @p3 Int = 6

SELECT DISTINCT [t5].[AccountGroupID], [t5].[AccountGroup] AS [AccountGroup1]
FROM [dbo].[TransmittalDetail] AS [t0]
INNER JOIN [dbo].[TransmittalHeader] AS [t1] ON [t1].[TransmittalHeaderID] = 
   [t0].[TransmittalHeaderID]
INNER JOIN [dbo].[LineItem] AS [t2] ON [t2].[LineItemID] = [t0].[LineItemID]
LEFT OUTER JOIN [dbo].[AccountType] AS [t3] ON [t3].[AccountTypeID] = 
   [t2].[AccountTypeID]
LEFT OUTER JOIN [dbo].[AccountCategory] AS [t4] ON [t4].[AccountCategoryID] = 
   [t3].[AccountCategoryID]
LEFT OUTER JOIN [dbo].[AccountGroup] AS [t5] ON [t5].[AccountGroupID] = 
   [t4].[AccountGroupID]
LEFT OUTER JOIN [dbo].[AccountSummary] AS [t6] ON [t6].[AccountSummaryID] = 
   [t5].[AccountSummaryID]
WHERE ([t1].[TransmittalEntityID] = @p0) AND ([t1].[DateRangeBeginTimeID] = @p1) 
   AND ([t1].[ScenarioID] = @p2) AND ([t6].[AccountSummaryID] = @p3)

Now, the truly bizarre part is that if I execute that SQL in Management Studio, it returns 11 rows in 3 seconds, yet the linq query that generates it will timeout after 30 seconds of activity.

Specifying the Take parameter just doesn't make much sense. Am I hitting some bug?

Note: The code times out without the Take() parameter whether i execute it from my app, or Linqpad. Likewise, in both app and linqpad it works fine with a Take() parameter. Also, without the distinct it only returns 19 rows.

+1  A: 

Compare the execution plan for the query with and without the .Take (top n). You probably got a bad execution plan for the query the first time, and adding Take just changed the query so it was compiled again.

Drop the execution plan with sp_recompile or dbcc freeproccache and see if that makes a difference.

You can also use a profiler to extract more information about query cost, execution plans etc...

KristoferA - Huagati.com
I'm not sure I understand. If I execute the SQL generated by the L2S query, there is no problem. Shouldn't the execution plan of the generated SQL be the same as the L2S query?
Mystere Man
The Linq-to-SQL query is translated to SQL. If it times out, the particular TSQL it was translated to is probably cached in the execution plan cache with a less-than-optimal execution plan. sp_recompile on one of the involved tables, or dbcc freeproccache will force a new execution plan...
KristoferA - Huagati.com
...oh, and using a profiler you can get the exact SQL query that L2S is hitting the database with. The one in your question, starting with the "declare " statements is not what L2S is hitting the db with. L2S would use sp_executesql to run it, to ensure it ends up in the stored proc plan cache...
KristoferA - Huagati.com
I'm still not getting it. I am executing the exact same SQL that the query is translated to, and it runs fine in SQL Management Studio. Also in Linqpad (executing SQL Query). Shouldn't that sql, which is the identical SQL that's generated also be having the same problem if it were a cached execution plan issue?
Mystere Man
Again, L2S does not generate a SQL statement that starts with "declare ....". That portion is generated by whatever tool you used to extract the query. (Linqpad?). The actual statement sent by L2S to the database should look something like "exec sp_executesql N'SELECT DISTINCT [t5]....', '@p0 int, @p1 int, @p2 int', @p0=196, @p1=20100101, ... ". Running something else than exactly the same query that L2S hits the database with, you're not using the same cached plan and so will not see the same behavior in case a random bad plan has been cached...
KristoferA - Huagati.com
Anyways, hook up SQL Profiler to the database, capture the exact SQL statement and then run that in management studio. You'll probably see the same behavior then. And/or try dbcc freeproccache and see if the performance of the L2S query improves...
KristoferA - Huagati.com
I'm leery about doing anything to the database, as it's a production database. Also, I don't seem to have the profiler installed here with the sql management tools. Will the freeproccache do anything to the server?
Mystere Man
Yes, freeproccache will drop all cached execution plans, so all cached plans will be recompiled the next time the queries for them are executed. This can be a bad thing if the server has a high CPU load but shouldn't make a big difference if it doesn't. As an alternative you can use sp_recompile, e.g. " sp_recompile 'dbo.TransmittalHeader' ". That will force only queries that read from that table to get new execution plans on the next run...
KristoferA - Huagati.com
That did it, thanks a ton.
Mystere Man