Время выполнения SQL-запроса быстрое, но выборка строк медленная

Это вопрос из двух частей, но сначала немного справочной информации:

У меня есть запрос TSQL в Sybase, который сообщает о времени выполнения 0,328 секунды, однако для получения примерно 5000 строк требуется около 20-30 секунд. Запрос имеет два подзапроса и левое внешнее соединение.

Запрос выглядит примерно так:

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')

Что я делаю, так это выбираю записи клиентов на основе даты последнего изменения любой связанной контактной информации. ContactPhone и ContactEmail могут содержать x записей для любой заданной строки в CustomerContact. Таблица Customer соответствует таблице CustomerContact.

Теперь мой вопрос:

  1. Почему Sybase сообщает о времени выполнения 0,328 секунды, но на самом деле для получения строк в запросе требуется около 30 секунд?

  2. Что я могу сделать, чтобы оптимизировать этот запрос?

Моей первой мыслью было добавить индексы к столбцам LastModified, но я имею дело с небольшим количеством записей.

Моя вторая мысль заключается в том, что подзапросы замедляют работу, и что я должен преобразовать их в соединения. Но я не могу использовать агрегатную функцию max в условии соединения, так как же мне получить только максимальную строку в моем соединении?

Спасибо


person dmck    schedule 09.04.2011    source источник
comment
Какую версию SQL Server вы используете? Я опубликую более быструю версию CTE, если вы используете 2005+.   -  person Hogan    schedule 09.04.2011
comment
Вы действительно должны предоставить план запроса / анализ затрат вместе с любым запросом на помощь в оптимизации запроса. См. infocenter.sybase.com /помощь/тема/   -  person eevar    schedule 09.04.2011
comment
@Hogan - с помощью Sybase, но спасибо, что нашли время дать ответ.   -  person dmck    schedule 09.04.2011


Ответы (3)


Я предполагаю, что 2 коррелированных подзапроса в предложении select не выполняются до тех пор, пока не будут возвращены строки. В целом следует избегать коррелированных подзапросов, поскольку они имеют тенденцию быть медленными, конечно, всегда есть исключения!

Попробуйте переместить ContactPhone и Contact Email в объединенный подзапрос.

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
person Paul Creasey    schedule 09.04.2011
comment
Опечатка - у вас есть два поля с именем EmailLastModified в операторе выбора: D - person Hogan; 09.04.2011
comment
Это сработало, похоже, вы были правы насчет подзапросов в операторе select. - person dmck; 09.04.2011

Теперь я вижу, что он использует SYBASE, а не SQL Server (может быть и TSQL), но я оставлю ответ другим, кто использует продукт MS.

Вот версия CTE. Работает так же, как версия Пола, но немного легче читать:

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
person Hogan    schedule 09.04.2011
comment
CTE поддерживаются в sybase везде, но не в ASE, я думаю, хотя и не уверен. - person Paul Creasey; 09.04.2011
comment
Adaptive Server Enterprise, версия sybase - person Paul Creasey; 09.04.2011

person    schedule
comment
что-то вроде cc.LastName и cc.FirstName не включено в предложение GROUP BY или цель обострения. - person Hogan; 10.04.2011