Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQL Server: IF EXISTS massively slowing down a query

(SQL Server 2012 being used)

I found some topics on query optimization, and comparing EXISTS to COUNT, but I couldn't find this exact problem.

I have a query that looks something like this:

select * from
tblAccount as acc
join tblUser as user on acc.AccountId = user.AccountId
join tblAddress as addr on acc.AccountId = addr.AccountId
... **a few more joins**
where acc.AccountId in (
    select * accountid from
    (select accountid, count(*) from tblUser
    where flag = 1
    group by accountId) as tbl where c != 1

This query runs in an instant (although the db is quite big, around 70Gb).

When I wrap the query in an EXISTS as in:

if exists
(
  **Exact same query as above**
)
begin
RAISERROR('Account found without exactly one flagged user.', 16, 1);
end
else
begin
  print 'test passed.'
end

Suddenly the query takes about 5-6 seconds to complete. I've tried specifying IF EXISTS (SELECT TOP 1 FROM... and also tried NOT EXISTS (which was even slower). But neither work to speed this up.

If the normal select query completes basically instantly, then does anyone know why wrapping it in the EXISTS causes so much extra computation? And/or anyone have any ideas to work around this (I'm just trying to throw an error if any records are found at all by the original query).

Thanks!

like image 562
John Darvill Avatar asked Jun 24 '15 16:06

John Darvill


2 Answers

Did you try running the original query with TOP 1? most likely it will be just as slow.

Sometimes when the optimizer thinks that something is very likely and going to return a vast set of data with little effort (i.e. almost all records are going to get returned), it chooses mostly loop joins because it only needs to get the first one and a loop join is good for only getting a couple records. When that turns out to not be true, it takes forever and a day to get results.

In your case, it sounds like it's very rare, so this choice hurts badly. Try instead doing something like SELECT @count = COUNT(*) FROM ... and then checking if that count is non-zero.

like image 61
Tim Tom Avatar answered Sep 23 '22 11:09

Tim Tom


I've fought this issue as well.

The query was 10ms when I ran it on its own but once I put it in the If Exists it went to 4 minutes. No matter what I tried it didn't go back to 10ms. The issue was re-produced on 4 different servers but not on 2 servers. Servers all had the same db backup and same patch level of mssql 2012. servers were on different OS and varying hardware settings.

I tried

  • adjusting the max memory grant - no affect
  • changing the threshold for parallelism - no affect
  • rewrite the query to make it simpler - no affect
  • use top 1 - no affect
  • cleared the cache between changes - no affect
  • break the query into some indexed views where I could (can't do it to parts using outer join) - no affect
  • applied recommended missing index - reduced time from 4 to 3 minutes but still not the 10 ms I expected.
  • change the outer join to a where not in (sub-query) - no affect
  • run sp_updateStats - no affect

The only solution that worked for me was to put the results in a temp table and do the if exists against that temp table.

SELECT top 1 1 AS junk INTO #me FROM yourCraxyQueryHere IF EXISTS ( SELECT 1 FROM #me ) SELECT GETDATE()

hope this helps

like image 43
Jack B Avatar answered Sep 20 '22 11:09

Jack B