SQL Server Query time out depending on Where Clause
I have a query that uses 3 functions and a few different views beneath it, which are too complex to post here. The odd thing I am experiencing is when running the top level query, having more than 1 search key is causing the query to take around an hour to run, where splitting the query in two takes about 5 seconds per query.
Here is the top level query:
Select *
from dbo.vwSimpleInvoice i
inner join dbo.vwRPTInvoiceLineItemDetail d on i.InvoiceID = d.InvoiceID
When I add this where clause:
Where i.InvoiceID = 109581
The query takes about 3 seconds to run. Similarly when I add this where clause:
Where i.InvoiceID = 109582
it takes about 3 seconds.
When I add this where clause though:
Where i.InvoiceID in (109581, 109582)
I have had to kill the query after about 50 minutes, and it never returns any results.
This is occurring on a remote client's server running SQL Server 2008 R2 Express. When I run it locally (also on SQL Server 2008 R2 Express), I don't get the massive delay, the last where clause takes about 30 seconds to return. The client has a lot more data than me though.
Any idea where to start troubleshooting this?
Edit:
After the comments below I rebuilt indexes and stats, which improved performance of the first 2 where clauses, but had no effect on the third. I then played around with the query, and discovered that if I rewrote it as:
Select *
from dbo.vwSimpleInvoice i
inner join
(Select * from dbo.vwRPTInvoiceLineItemDetail) d on i.InvoiceID = d.InvoiceID
Where i.InvoiceID in (109581, 109582)
Performance returns to expected levels, around 200 ms. I am now more mystified than ever as to what is occurring...
Edit 2:
Actually, I am wrong. It wasn't rewriting the query like that, I accidentally changed the Where Clause during the rewrite to:
Where d.InvoiceID in (109581, 109582)
(Changed i
to d
).
Still at a bit of a loss as to why this makes such as massive difference on an Inner Join?
Further edit:
Playing around with this even further, I still cannot understand it.
Select InvoiceId from tblInvoice Where CustomerID = 2000
returns:
80442, 4988, 98497, 102483, 102484, 107958, 127063, 168444, 168531, 173382, 173487, 173633, 174013, 174160, 174240, 175389
Select * from dbo.vwRPTInvoiceLineItemDetail
Where InvoiceID in
(80442, 4988, 98497, 102483, 102484, 107958, 127063, 168444, 168531, 173382, 173487, 173633, 174013, 174160, 174240, 175389)
Runs: 31 Rows returned 110 ms
Select * from dbo.vwRPTInvoiceLineItemDetail
Where InvoiceID in
(Select InvoiceId from tblInvoice Where CustomerID = 2000)
Runs: 31 rows returned 65 minutes
Solution 1:
The Problem you are experiencing is (almost certainly) due to a cached query plan, which is appropriate for some version of parameters passed to the query, but not for others (aka Parameter Sniffing).
This is a common occurance, and is often made worse by out of date statistics and/or badly fragmented indexes.
First step: ensure you have rebuilt all your indexes and that statistics on non-indexed columns are up to date. (Also, make sure your client has a regularly scheduled index maintenance job)
exec sp_msforeachtable "DBCC DBREINDEX('?')"
go
exec sp_msforeachtable "UPDATE STATISTICS ? WITH FULLSCAN, COLUMNS"
go
This is the canonical reference: Slow in the Application, Fast in SSMS?
If the problem still exists after rebuilding indexes and updating statistics, then you have a few options:
Use dynamic SQL (but read this first: The Curse and Blessings of Dynamic SQL)
Use
OPTIMIZE FOR
Use
WITH(RECOMPILE)