SQL Query execution time fast, but fetching rows is slow
This is a two part question, but first some background information:
I have a TSQL query in Sybase that reports an execution time of 0.328 seconds, however its ta开发者_如何转开发king around 20-30 seconds to retrieve approximately 5000 rows. The query has two subquery's and a left outer join.
The query looks roughly like this:
SELECT CustomerContact.Id, Customer.Name, ...
, CustomerContacts.LastName, CustomerContacts.FirstName
, ( SELECT max(LastModified)
FROM ContactPhone
WHERE ContactPhone.ContactID = CustomerContact.ID
) as PhoneLastModified
, ( SELECT max(LastModified)
FROM ContactEmail
WHERE ContactEmail.ContactID = CustomerContact.ID
) as EmailLastModified
FROM CustomerContacts
LEFT OUTER JOIN Customer
ON Customer.ID = CustomerContact.CustomerId
WHERE (PhoneLastModified > '2011-01-01'
OR EmailLastModified > '2011-01-01')
What I am doing is selecting customer records based on the last modified date of any associated contact information. ContactPhone and ContactEmail can contain x number of records for any given row in CustomerContact. The Customer table is one-to-one with CustomerContact.
Now my question:
How come Sybase reports an execution time of 0.328 seconds, but it's actually taking closer to 30 seconds to retrieve the rows in the query?
What can I do to optimize this query?
My first thought is to add indexes to the LastModified columns, but I'm dealing with a small number of records.
My second thought is that the subquery's are slowing things down, and that I should convert them into joins. But I can't use the aggregate function max in the Join condition, so how do I get only the max row in my join?
Thanks
I'm guessing that the 2 correlated subqueries in the select clause don't execute until the rows are returned. In general correlated subqueries should be avoided, as they tend to be slow, of course there are always exceptions!
Try moving ContactPhone and Contact Email into a joined subquery.
SELECT
cc.Id,
c.Name,
... ,
cc.LastName, CustomerContacts.FirstName,
cp.LastModified PhoneLastModified
ce.LastModified EmailLastModified
FROM
CustomerContacts cc
LEFT OUTER JOIN
Customer c
ON
c.ID = cc.CustomerId
INNER JOIN
(SELECT
ContactId,
max(LastModified) as LastModified
FROM
ContactPhone
WHERE
LastModified > '2011-01-01'
GROUP BY
ContactId ) cp
ON
cp.ContactID = cc.ID
INNER JOIN
(SELECT
ContactId,
max(LastModified) as LastModified
FROM
ContactEmail
WHERE
LastModified> '2011-01-01'
GROUP BY
ContactId ) ce
ON
ce.ContactID = cc.ID
I see now he is using SYBASE not SQL Server (TSQL could be either), but I'll leave the answer for others who are using the MS product.
Here is the CTE version. Works the same as Paul's version but slightly easier to read:
WITH MaxContactPhone AS
(
SELECT max(LastModified) as LastModified, ContactID
FROM ContactPhone
WHERE LastModified> '2011-01-01'
GROUP BY ContactID
), MaxContactEmail AS
(
SELECT max(LastModified) as LastModifed, ContactID
FROM ContactEmail
WHERE LastModified> '2011-01-01'
GROUP BY ContactID
)
SELECT CustomerContact.Id, Customer.Name, ... , CustomerContacts.LastName,
CustomerContacts.FirstName,
MaxContactPhone.LastModified as PhoneLastModified,
MaxContactEmail.LastModified as EmailLastModified
FROM CustomerContacts
LEFT OUTER JOIN Customer ON Customer.ID = CustomerContact.CustomerId
JOIN MaxContactPhone ON CustomerContact.CustomerId = MaxContactPhone.ContactID AND
JOIN MaxContactEmail ON CustomerContact.CustomerId = MaxContactEmail.ContactID
SELECT cc.ID, cu.Name, ...
, cc.LastName, cc.FirstName
, g.PhoneLastModified
, g.EmailLastModified
FROM CustomerContacts cc
LEFT JOIN Customer cu
ON cu.ID = cc.CustomerID
JOIN
( SELECT cc.ID
, max(cp.LastModified)
AS PhoneLastModified
, max(ce.LastModified)
AS EmailLastModified
FROM CustomerContacts cc
LEFT JOIN ContactPhone cp
ON cp.ContactID = cc.ID
LEFT JOIN ContactEmail ce
ON ce.ContactID = cc.ID
GROUP BY cc.ID
HAVING ( PhoneLastModified > '2011-01-01'
OR EmailLastModified > '2011-01-01' )
) AS g
ON g.Id = cc.id
精彩评论