I have a really odd query involving a join to a complex view. I analyzed the heck out of the view, built some indexes, and got the query working in under a second when run from MSSQL Management Studio. However, when run from Perl via ODBC, the exact same query takes around 80 seconds to return.
I've dumped almost 8 hours into this and it continues to baffle me. In that time I've logged the query from Perl and copied it verbatim into Studio, I've wrapped it in a stored procedure (which makes it take a consistent 2.5 minutes from BOTH clients!), I've googled ODBC & MSSQL query caches, I've watched the query run via the Activity Monitor (it spends most of its time in the generic SLEEP_TASK wait state) and Profiler (the select statement gets one line which doesn't show up until it's done running), and I've started reading up on performance bottlenecks.
I haven't noticed this problem with any other queries from Perl and unfortunately we don't have a DBA on site. I'm a programmer who's done some DBA but I feel like I'm groping in the dark with this one. My best guess is that there is some sort of query cache available from Studio that the ODBC client can't access, but restarting Studio does not make the query's first execution take longer so it doesn't look like it's just because each new ODBC connection starts with an empty cache.
Without going into the view definitions, the base query is very simple:
SELECT * FROM VIEW1 LEFT OUTER JOIN VIEW2 WHERE SECTION = ? AND ID = ?
The delay goes away when I drop VIEW2, but I need the data from that view. I've already rewritten the view three times in attempts to simplify and improve efficiency but this feels kinda like a dead end since the query runs fine from Studio. The query only returns a single row but even dropping the ID criteria and selecting all 56k rows for an entire section only takes 40 seconds from Studio. Any other ideas?
Edit 2/8: The article @Remus Rusanu linked was pretty clear, but I'm afraid it didn't quite apply. It now seems pretty clear that it's not ODBC at all, but that when I hard-code arguments vs. parametrize them, I get different execution plans. I can reproduce this in SSMS:
SELECT * FROM VIEW1 LEFT OUTER JOIN VIEW2 WHERE SECTION = 'a' AND ID = 'b'
is 100 times faster than
DECLARE @p1 VARCHAR(8), @p2 VARCHAR(3)
SET @p1 = 'a'
SET @p2 = 'b'
SELECT * FROM VIEW1 LEFT OUTER JOIN VIEW2 WHERE SECTION = @p1 AND ID = @p2
Unfortunately, I'm still at a loss to explain why the first should get an execution plan that takes two orders of magnitude less time than the parametrized version, for any values of SECTION & ID I can throw at it. It may be a deficiency in SQL Server, but it just seems stupid that the inputs are known in both places yet the one takes so much longer. If SQL server recomputed the parametrized plan from scratch every time, as it must be doing for the different constant values I am supplying, it would be 100 times faster. None of the RECOMPILE options suggested by the article seem to help either.
I think @GSerg called it below. I've yet to prove that it doesn't happen with the window function used externally to the view, but he describes the same thing and the timing discrepancy remains baffling.
If I don't run out of time to work on this, I'll try to adapt some of the article's advice and force the constant execution plan on the parametrized version, but it seems like an awful lot of what should be unnecessary trouble.