SQL Server Performance

Same SP, different plans

Discussion in 'T-SQL Performance Tuning for Developers' started by flachance, Jan 23, 2003.

  1. flachance New Member

    I'm really stumped as to why this would happen at all. I have a web site that makes a call to a stored procedure. When the call is made from the web site, the following plan is generated (taken from SQL Profiler):<br /><pre>exec s_Inventory NULL, NULL, '', '', '', '', '', '', '', 'Pawn Receipt', '37992', '16', NULL, NULL<br /><br />Execution Tree<br />--------------<br />Hash Match(Flow Distinct, HASH<img src='/community/emoticons/emotion-6.gif' alt=':(' />[Union1016], [Union1017], [Union1018], [Union1019], [Union1020], [Union1021], [Union1022], [Union1023], [Union1024], [Union1025], [Union1026], [Union1027], [Union1028], [Union1029], [Union1030], [Union1031], [Union1032]), RESIDUAL<img src='/community/emoticons/emotion-6.gif' alt=':(' />((((((((((((((([Union1016]=[Union1016] AND [Union1017]=[Union1017]) AND [Union1018]=[Union1018]) AND [Union1019]=[Union1019]) AND [Union1020]=[Union1020]) AND [Union1021]=[Union1021]) AND [Union1022]=[Union1022]) AND [Union1023]=[Union1023]) AND [Union1024]=[Union1024]) AND [Union1025]=[Union1025]) AND [Union1026]=[Union1026]) AND [Union1027]=[Union1027]) AND [Union1028]=[Union1028]) AND [Union1029]=[Union1029]) AND [Union1030]=[Union1030]) AND [Union1031]=[Union1031]) AND [Union1032]=[Union1032]))<br /> |--Concatenation<br /> |--Nested Loops(Left Outer Join, OUTER REFERENCES<img src='/community/emoticons/emotion-6.gif' alt=':(' />[T].[TransID]) WITH PREFETCH)<br /> | |--Bookmark Lookup(BOOKMARK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[Bmk1000]), OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Customer] AS [C]))<br /> | | |--Nested Loops(Left Outer Join, OUTER REFERENCES<img src='/community/emoticons/emotion-6.gif' alt=':(' />[T].[CustID]) WITH PREFETCH)<br /> | | |--Nested Loops(Inner Join, OUTER REFERENCES<img src='/community/emoticons/emotion-6.gif' alt=':(' />[T].[TransID], [P].[PropertyID]) WITH PREFETCH)<br /> | | | |--Nested Loops(Inner Join, WHERE<img src='/community/emoticons/emotion-6.gif' alt=':(' />([P].[LocationOrgID]=NULL AND [T].[OrgID]=16) OR [P].[LocationOrgID]=16))<br /> | | | | |--Clustered Index Scan(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Transactions].[IX_Transactions_OrgIDCustID] AS [T]), WHERE<img src='/community/emoticons/emotion-6.gif' alt=':(' />[T].[OrgTransID]=37992))<br /> | | | | |--Index Scan(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Property].[IX_PropertyStatus] AS [P]), WHERE<img src='/community/emoticons/emotion-6.gif' alt=':(' />[P].[LocationOrgID]=NULL OR [P].[LocationOrgID]=16))<br /> | | | |--Index Seek(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[TransItemsXRF].[IX_TransItemsXRF_Property] AS [Tx]), SEEK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[Tx].[TransID]=[T].[TransID] AND [Tx].[PropertyID]=[P].[PropertyID]) ORDERED FORWARD)<br /> | | |--Index Seek(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Customer].[PK_Customer] AS [C]), SEEK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[C].[CustId]=[T].[CustID]) ORDERED FORWARD)<br /> | |--Clustered Index Seek(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Purchases].[PK_Purchase]), SEEK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[Purchases].[TransID]=[T].[TransID]) ORDERED FORWARD)<br /> |--Nested Loops(Left Outer Join, OUTER REFERENCES<img src='/community/emoticons/emotion-6.gif' alt=':(' />[T].[CustID]) WITH PREFETCH)<br /> |--Filter(WHERE<img src='/community/emoticons/emotion-6.gif' alt=':(' />[T].[OrgTransID]=37992))<br /> | |--Bookmark Lookup(BOOKMARK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[Bmk1010]), OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Transactions] AS [T]))<br /> | |--Nested Loops(Inner Join, OUTER REFERENCES<img src='/community/emoticons/emotion-6.gif' alt=':(' />[TR].[TransID]) WITH PREFETCH)<br /> | |--Nested Loops(Inner Join, OUTER REFERENCES<img src='/community/emoticons/emotion-6.gif' alt=':(' /><i>.[PropertyID]) WITH PREFETCH)<br /> | | |--Nested Loops(Inner Join, OUTER REFERENCES<img src='/community/emoticons/emotion-6.gif' alt=':(' /><I>.[PropertyID]) WITH PREFETCH)<br /> | | | |--Table Scan(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Inventory] AS <I>), WHERE<img src='/community/emoticons/emotion-6.gif' alt=':(' /><I>.[OrgID]=16))<br /> | | | |--Index Seek(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Property].[IX_Property_InventorySearch] AS [P]), SEEK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[P].[PropertyID]=<I>.[PropertyID]) ORDERED FORWARD)<br /> | | |--Clustered Index Seek(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[TransItemsXRF].[PK_TransItemsXRF] AS [TR]), SEEK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[TR].[PropertyID]=<I>.[PropertyID]) ORDERED FORWARD)<br /> | |--Index Seek(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Transactions].[IX_Transactions_TimeStamp] AS [T]), SEEK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[T].[TransID]=[TR].[TransID]) ORDERED FORWARD)<br /> |--Clustered Index Seek(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Customer].[IX_Customer_CustIDLastName] AS [C]), SEEK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[C].[CustId]=[T].[CustID]) ORDERED FORWARD)<br /></pre><br /><br />But when I execute the <i>exact same</i> SP call from Query Analyzer, I get a different plan:<br /><br /><pre>Execution Tree<br />--------------<br />Merge Join(Union)<br /> |--Nested Loops(Left Outer Join, OUTER REFERENCES<img src='/community/emoticons/emotion-6.gif' alt=':(' />[T].[TransID]) WITH PREFETCH)<br /> | |--Sort(ORDER BY<img src='/community/emoticons/emotion-6.gif' alt=':(' />[C].[LastName] ASC, [C].[FirstName] ASC, [C].[CustId] ASC, [T].[TimeStamp] ASC, [Tx].[PropertyID] ASC, [T].[TransID] ASC))<br /> | | |--Merge Join(Left Outer Join, MANY-TO-MANY MERGE<img src='/community/emoticons/emotion-6.gif' alt=':(' />[T].[CustID])=([C].[CustId]), RESIDUAL<img src='/community/emoticons/emotion-6.gif' alt=':(' />[C].[CustId]=[T].[CustID]))<br /> | | |--Filter(WHERE<img src='/community/emoticons/emotion-6.gif' alt=':(' />(([P].[LocationOrgID]=NULL AND [T].[OrgID]=16) OR [P].[LocationOrgID]=16) AND ([P].[LocationOrgID]=NULL OR [P].[LocationOrgID]=16)))<br /> | | | |--Bookmark Lookup(BOOKMARK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[Bmk1003]), OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Property] AS [P]))<br /> | | | |--Nested Loops(Inner Join, OUTER REFERENCES<img src='/community/emoticons/emotion-6.gif' alt=':(' />[Tx].[PropertyID]) WITH PREFETCH)<br /> | | | |--Nested Loops(Inner Join, OUTER REFERENCES<img src='/community/emoticons/emotion-6.gif' alt=':(' />[T].[TransID]))<br /> | | | | |--Sort(ORDER BY<img src='/community/emoticons/emotion-6.gif' alt=':(' />[T].[CustID] ASC))<br /> | | | | | |--Clustered Index Scan(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Transactions].[IX_Transactions_OrgIDCustID] AS [T]), WHERE<img src='/community/emoticons/emotion-6.gif' alt=':(' />[T].[OrgTransID]=37992))<br /> | | | | |--Index Seek(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[TransItemsXRF].[IX_TransItemsXRF_Property] AS [Tx]), SEEK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[Tx].[TransID]=[T].[TransID]) ORDERED FORWARD)<br /> | | | |--Index Seek(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Property].[PK_Property] AS [P]), SEEK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[P].[PropertyID]=[Tx].[PropertyID]) ORDERED FORWARD)<br /> | | |--Clustered Index Scan(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Customer].[IX_Customer_CustIDLastName] AS [C]), ORDERED FORWARD)<br /> | |--Clustered Index Seek(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Purchases].[PK_Purchase]), SEEK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[Purchases].[TransID]=[T].[TransID]) ORDERED FORWARD)<br /> |--Sort(DISTINCT ORDER BY<img src='/community/emoticons/emotion-6.gif' alt=':(' />[C].[LastName] ASC, [C].[FirstName] ASC, [C].[CustId] ASC, [T].[TimeStamp] ASC, [P].[PropertyID] ASC, [P].[Description] ASC, [T].[TransID] ASC, [P].[Stock] ASC, [P].[Status] ASC, [P].[Price] ASC, [TR].[Amount] ASC, [T].[TransType] ASC, [T].[VoidDate] ASC))<br /> |--Nested Loops(Left Outer Join, OUTER REFERENCES<img src='/community/emoticons/emotion-6.gif' alt=':(' />[T].[CustID]) WITH PREFETCH)<br /> |--Filter(WHERE<img src='/community/emoticons/emotion-6.gif' alt=':(' />[T].[OrgTransID]=37992))<br /> | |--Bookmark Lookup(BOOKMARK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[Bmk1010]), OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Transactions] AS [T]))<br /> | |--Nested Loops(Inner Join, OUTER REFERENCES<img src='/community/emoticons/emotion-6.gif' alt=':(' />[TR].[TransID]) WITH PREFETCH)<br /> | |--Nested Loops(Inner Join, OUTER REFERENCES<img src='/community/emoticons/emotion-6.gif' alt=':(' /><I>.[PropertyID]) WITH PREFETCH)<br /> | | |--Nested Loops(Inner Join, OUTER REFERENCES<img src='/community/emoticons/emotion-6.gif' alt=':(' /><I>.[PropertyID]) WITH PREFETCH)<br /> | | | |--Table Scan(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Inventory] AS <I>), WHERE<img src='/community/emoticons/emotion-6.gif' alt=':(' /><I>.[OrgID]=16))<br /> | | | |--Index Seek(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Property].[IX_Property_InventorySearch] AS [P]), SEEK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[P].[PropertyID]=<I>.[PropertyID]) ORDERED FORWARD)<br /> | | |--Clustered Index Seek(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[TransItemsXRF].[PK_TransItemsXRF] AS [TR]), SEEK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[TR].[PropertyID]=<I>.[PropertyID]) ORDERED FORWARD)<br /> | |--Index Seek(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Transactions].[IX_Transactions_TimeStamp] AS [T]), SEEK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[T].[TransID]=[TR].[TransID]) ORDERED FORWARD)<br /> |--Clustered Index Seek(OBJECT<img src='/community/emoticons/emotion-6.gif' alt=':(' />[BWIdent].[dbo].[Customer].[IX_Customer_CustIDLastName] AS [C]), SEEK<img src='/community/emoticons/emotion-6.gif' alt=':(' />[C].[CustId]=[T].[CustID]) ORDERED FORWARD)<br /></pre><br /><br />What gives? Why would SQL Server do that? As you might notice in the second execution plan, there is a UNION in the SQL. The stored procedure actually builds a string based on parameters passed which then gets executed using EXECUTE(). The first call times out on the web site (takes more then 60 seconds) but when I call the stored procedure from Query Analyzer, it takes about 4 seconds.<br /><br />Any help would be appreciated.<br /><br />Francois
  2. joechang New Member

    there might be some difference in the way NULL values are passed in
    you might want to print the string before executing it to see if there are any differences
    i am thinking that one cause of the poor performance is the hash join in the first plan,
    if a hash join involves many row (>10,000) in certain circumstances, SQL will spool results into a temp table even if the server has plenty of memory
    if the temp db is not setup for high thru-put, high row count hash joins will perform poorly
  3. bradmcgehee New Member

    I agree with joechang that the way the UNION is being run is causing the problem. Also, his idea about printing the string first is a very good idea to help diagnose this problem.

    While this may not be what is causing the problem in your case, I have seen cases where I have the same exact query use a HASH one time, and a MERGE the other, for no apparent reason. In some cases I traced it back to statistics not be updated often enough, and when the statistics were updated, things worked normally. In other cases, I was not able to find the cause of the problem, so I used a UNION hint to force the type of operation I wanted, and this solved the problem.

    ------------------
    Brad M. McGehee
    Webmaster
    SQL-Server-Performance.Com
  4. x002548 New Member

    In our experince (with java calling s/p's) they have a tendency to not handle nulls well (or at all). You seem to have a massive sql statement in there, can you post what it looks like? Maybe you can tune it for better efficiency.

    Brett
  5. flachance New Member

    I have come to the same conclusion as bradmcgehee and have used a union hint (OPTION (MERGE UNION)) and that fixed the problem. The query itself is dynamically generated by the stored procedure based on options choosen by the user. The resulting query is quite large. Here it is:



    -- Dynamic SQL
    SELECT Distinct C.LastName, C.FirstName, C.CustID, T.TimeStamp, P.PropertyID,
    P.Description, T.TransID, P.Stock, P.Status, P.Price, Tx.Amount,
    T.TransType, T.VoidDate, T.OrgID, T.OrgTransID, Purchases.PawnTicket,
    null AS Cost
    FROM Customer C RIGHT OUTER JOIN Transactions T ON C.CustId = T.CustID
    INNER JOIN TransItemsXRF Tx ON T.TransID = Tx.TransID
    INNER JOIN Property P ON Tx.PropertyID = P.PropertyID
    LEFT OUTER JOIN Purchases ON Purchases.TransID = T.TransID
    WHERE ((P.LocationOrgID IS NULL AND T.OrgID = 16) OR P.LocationOrgID = 16)
    AND T.OrgTransID = 37992

    UNION

    SELECT Distinct C.LastName, C.FirstName, C.CustID, T.TimeStamp, P.PropertyID,
    P.Description, T.TransID, P.Stock, P.Status, P.Price, TR.Amount,
    T.TransType, T.VoidDate, I.OrgID, T.OrgTransID, null, null
    FROM Property P INNER JOIN Inventory I ON P.PropertyID = I.PropertyID
    LEFT OUTER JOIN TransItemsXRF TR ON P.PropertyID = TR.PropertyID
    LEFT OUTER JOIN Transactions T ON TR.TransID = T.TransID
    LEFT OUTER JOIN Customer C ON T.CustID = C.CustID
    LEFT OUTER JOIN Purchases ON T.TransID = Purchases.TransID
    WHERE I.OrgID = 16 And T.OrgTransID = 37992

Share This Page