SQL-запрос выполняется медленно — анализ параметров

У меня есть простой запрос, в котором я возвращаю список заказов по диапазону дат. Этот запрос используется в отчете, который передает ему параметры (сайт, дата начала и дата окончания).

ALTER PROCEDURE [dbo].[Z_N_ECOM_ORDER_STATUS_DATERANGE]
    @Site VARCHAR(5),
    @FromDate DATETIME,
    @ToDate DATETIME
AS
BEGIN
    SET NOCOUNT ON;

    SELECT
        o.Company_Code,
        o.Division_Code,
        o.Control_Number,
        RTRIM(o.Customer_Purchase_Order_Number) AS Shopify_Num,
        CASE 
           WHEN p.PickTicket_Number IS NULL
              THEN i.PickTicket_Number 
              ELSE p.PickTicket_Number 
        END PickTicket_Number,
        i.Invoice_Number,
        o.Date_Entered,
        CASE
           WHEN ph.packslip IS NULL AND i.invoice_number IS NULL  
                AND P.pickticket_number IS NULL
              THEN 'Cancelled' 
           WHEN ph.packslip IS NULL AND i.invoice_number IS NULL 
                AND DATEADD(minute, 90, o.date_entered) > CURRENT_TIMESTAMP
              THEN 'Not Entered Yet'  
           WHEN ph.packslip IS NULL 
              THEN 'SHIPPED & UPLOADED' 
           ELSE RTRIM (z.status) 
        END Accellos_Status, 
        b.UPS_Tracking_Number Tracking_Number
    FROM
        [JMNYC-AMTDB].[AMTPLUS].[dbo].Orders o (nolock)
    LEFT JOIN
        [JMNYC-AMTDB].[AMTPLUS].[dbo].PickTickets p (nolock) ON o.Company_Code = p.Company_Code 
                                                         AND o.Division_Code = p.Division_Code 
                                                         AND o.Control_Number = p.Control_Number
    LEFT JOIN
        [JMNYC-AMTDB].[AMTPLUS].[dbo].Invoices i (nolock) ON o.Company_Code = i.Company_Code 
                                                      AND o.Division_Code = i.Division_Code 
                                                      AND o.Control_Number = i.Control_Number   
    LEFT JOIN
        [JMNYC-AMTDB].[AMTPLUS].[dbo].box b (nolock) ON o.Company_Code = b.Company_Code 
                                                AND o.Division_Code = b.Division_Code 
                                                AND i.PickTicket_Number = b.PickTicket_Number
    LEFT JOIN
        pickhead ph (nolock) ON p.PickTicket_Number = ph.packslip
    LEFT JOIN
        Z_Status z (nolock) ON ph.PROCSTEP = z.procstep
    WHERE 
        o.Company_Code = LEFT(@Site, 2)
        AND o.Division_Code = RIGHT(@Site, 3) 
        AND o.Customer_Number = 'ecom2x'
        AND o.Date_Entered BETWEEN @FromDate AND DATEADD(dayofyear, 1, @ToDate)
    ORDER BY 
        o.date_entered DESC
END

Проблема с этим запросом в том, что он занимает слишком много времени, а проблемные строки

 WHERE 
     o.Company_Code = LEFT(@Site, 2)
     AND o.Division_Code = RIGHT(@Site, 3)

Формат переменной сайта — что-то вроде «09001» или «03001», где левая сторона — это компания, а правая — подразделение.

Потому что, когда я запускаю этот запрос с жестко закодированными значениями, он выполняется почти мгновенно. Когда я использую параметры, это занимает минуты.

Итак, я просмотрел его и обнаружил обнюхивание параметров. Поэтому я добавил следующую строку после оператора begin.

DECLARE @LocalSite VARCHAR(5) = CAST(@Site AS VARCHAR(5))

Тем не менее, он по-прежнему работает очень медленно.

Мой новый оператор where будет

WHERE 
    o.Customer_Number = 'ecom2x'
    AND o.Date_Entered BETWEEN @FromDate AND DATEADD(dayofyear, 1,  @ToDate)
    AND ((@LocalSite = '00000') OR (O.Company_Code = LEFT(@LocalSite, 2) AND O.Division_Code = RIGHT(@LocalSite, 3))) 
order by o.date_entered desc*

Я также хочу, чтобы у пользователя была возможность выбора всех сайтов, в результате чего переменная сайта будет равна «00000», и поэтому он не должен запускать проверку кода компании/подразделения. Этот текущий оператор where делает запрос очень медленным.

Кто-нибудь знает, что я делаю неправильно?


person Natan    schedule 25.01.2019    source источник
comment
Для решения проблем с производительностью было бы полезно опубликовать план выполнения. Вот отличный способ сделать это. brentozar.com/pastetheplan Тем временем я настоятельно рекомендую вам не разбрызгивать NOLOCK повсюду. Это НЕ волшебная кнопка «быстро», у нее есть очень серьезный багаж. blogs.sentryone.com/aaronbertrand/bad-habits-nolock-everywhere. Также, возможно, стоит прочитать о BETWEEN. sqlblog.org/2011/ 19.10.   -  person Sean Lange    schedule 25.01.2019
comment
Проанализируйте план запроса, я думаю, у вас есть некоторые отсутствующие индексы, которые вызывают медленное выполнение. Планы выполнения хранимых процедур кэшируются, это можно отключить, добавив WITH RECOMPILE в заголовок SP.   -  person Karel Frajták    schedule 25.01.2019
comment
Мой начальник попросил @SeanLange установить nolock везде, я не могу это контролировать. Кроме того, вам нужен план выполнения запроса с жестко заданными значениями или хранимой процедуры? Потому что это медленно только как хранимая процедура с параметрами. brentozar.com/pastetheplan/?id=BkHaPktQE   -  person Natan    schedule 25.01.2019
comment
Проблема, вероятно, в том, что вы используете не сам код, а сначала применяете функцию, поэтому любые индексы игнорируются, и вместо этого вы получаете сканирование таблицы (что делает ее недоступной для поиска, см. sqlconsulting.com/archives/understanding-search-arguments). Это могло бы быть намного быстрее, если бы у вас была таблица поиска с подразделениями, компаниями и сайтами, чтобы она могла фактически искать значение «09001» в индексе.   -  person PeterDeV    schedule 25.01.2019
comment
@SeanLange Проблема не в отсутствии блокировки или между ними, а определенно в функциях Left и Right.   -  person Natan    schedule 25.01.2019
comment
Я НЕ предполагал, что проблема была NOLOCK или BETWEEN. Но эти намеки на отсутствие блокировки, скорее всего, плохая идея. Поймите, иногда вы должны. И предложил прочитать промежду, потому что это причиняет людям много боли, особенно свидания. Если это быстро в SSMS и медленно, как процедура, это почти наверняка плохой анализ параметров, как вы, кажется, догадались. Левые и правые здесь не проблема, потому что они смотрят на параметр. Функции в предложении where для столбцов плохи, а не для параметров.   -  person Sean Lange    schedule 25.01.2019
comment
@Sean Lange, я исправляюсь, я неправильно прочитал код. Сам запрос может просто искать Company_Code или Division_Code в индексе, поэтому я согласен, что это должен быть анализ параметров и что он не имеет ничего общего с аргументами поиска.   -  person PeterDeV    schedule 25.01.2019
comment
Вы можете сразу определить, является ли это проблемой перехвата параметров... переопределить процедуру с помощью RECOMPILE... что не позволяет SQL сохранить план запроса для процедуры. Если слетает... в этом проблема... и перекомпиляция скорее всего отходит на второй план как статья расходов.   -  person Clay    schedule 25.01.2019
comment
Я также пробовал перекомпилировать, запрос не стал быстрее. @SeanLange Я не понимаю, если вы говорите, что левое и правое не проблема, в чем может быть проблема?   -  person Natan    schedule 25.01.2019
comment
Опубликуйте изображение фактического плана запроса, если вы можете... обязательно указать где-нибудь ;-)   -  person Clay    schedule 26.01.2019
comment
@SeanLange, если проблема с обнюхиванием параметров (что я тоже думаю сейчас), почему использование решения для локальной переменной, похоже, ничего не делает? Я прочитал статью из трех частей, на которую вы ссылались, но мой запрос по-прежнему выполняется так же медленно, как и раньше.   -  person Natan    schedule 28.01.2019
comment
Я не могу ответить на этот вопрос, потому что не знаю, как выглядит новый код. Я видел (и делал сам) в прошлом, пытаясь справиться с обнюхиванием и использованием локальных переменных, но забыл изменить сам запрос. Может быть, это произошло здесь? Можете ли вы опубликовать обновленный запрос, чтобы мы могли посмотреть?   -  person Sean Lange    schedule 28.01.2019


Ответы (2)


Можете ли вы попытаться избежать использования LEFT() и RIGHT(), объявив несколько переменных и присвоив значения этим переменным, а затем используя их в операторе SELECT?

подсказка, ОПТИМИЗИРОВАННАЯ ДЛЯ НЕИЗВЕСТНЫХ, чтобы избежать прослушивания параметров:

option (OPTIMIZE FOR (@p1 UNKNOWN, @p2 UNKNOWN))

Где p1 и p2 эти две переменные, упомянутые выше

Я также хочу, чтобы у пользователя была возможность выбора всех сайтов, в результате чего переменная сайта будет равна «00000», и поэтому он не должен запускать проверку кода компании/подразделения. Этот текущий оператор where делает запрос очень медленным.

Это можно оптимизировать, заменив текущий оператор SELECT оператором IF, который использует два оператора SELECT. Если значение равно 00000, просто избегайте проверки компании и подразделения, иначе запустите тот же выбор, но с этими дополнительными проверками.

Еще одна поразительная вещь — запросы связанных серверных объектов с последующим присоединением к локальным таблицам. Подумайте о том, чтобы разделить это на отдельный шаг, например, сохранив данные во временной таблице (не в табличной переменной!) в качестве промежуточного результата. Затем временная таблица будет объединена с локальными объектами. Это может повысить точность плана запроса из-за более точных оценок.

person Alexander Volok    schedule 25.01.2019
comment
Поскольку OP использует левый и правый для просмотра параметров, так что здесь вряд ли возникнут какие-либо проблемы. - person Sean Lange; 25.01.2019
comment
Я думаю, что проблема заключается в прослушивании параметров, но все перечисленные меры могут быть применены - person Alexander Volok; 25.01.2019

Вы пытались взять левое и правое значения параметра @site в двух разных переменных и использовать эти переменные в SP.

Например.

Declare @compcode as varchar(2)
Declare @divcode as varchar(3)
Set @compcode=LEFT(@Site, 2)
Set @divcode=RIGHT(@Site, 3)

Ваше состояние

WHERE 
o.Company_Code = @compcode
AND o.Division_Code = @divcode
person sheela w    schedule 25.01.2019