Оптимизировать хранимую процедуру T-SQL - PullRequest
0 голосов
/ 07 июня 2019

Мне нужно как-то оптимизировать мой t-sql запрос, но у меня нет большого опыта в этом. Надеюсь на вашу поддержку. Выполнение этого длится долго, если я передам много RetailersID в условие где. Видимо, я должен переписать подзапрос в первом

Select LeadRetailerId, Count(*) as NumberGreenLeads
from  [MBCH_LMT].[lead].[Contact] a
inner join [MBCH_LMT].[lead].[ContactActivity] b
on a.UID = b.ContactUID
where 1=1
    AND b.LeadStatusDate > DATEADD(Year, -1, GetDate())
    AND a.LeadRetailerId in (@RetailerId)
    group by a.LeadRetailerId


Select Table_1.LeadRetailerId, Table_1.EscalationLevel ,Count(Table_1.EscalationLevel) as Number
from (
    select a.LeadRetailerId, max(EscalationLevel) as EscalationLevel
    from [MBCH_LMT].[lead].[Contact]  a
    inner join [MBCH_LMT].[lead].[ContactActivityEscalationHistory] b
    on b.ContactUID = a.UID
    where 1=1
    AND a.LeadRetailerId in (@RetailerId) 
    group by ContactUID, LeadRetailerId) as Table_1
Group by EscalationLevel, LeadRetailerId

Ответы [ 4 ]

1 голос
/ 07 июня 2019

Я бы порекомендовал взять значение DATEADD(Year,-1,GETDATE()) и поместить его в переменную, что упростит эту часть объединения. Кроме того, в зависимости от количества значений в @RetailerId, которые могут быть помещены в переменную таблицы или аналогичную, чтобы облегчить ее использование в запросах. Затем я отобразил бы фактический план выполнения и посмотрел бы, рекомендуются ли какие-либо индексы как отсутствующие.

0 голосов
/ 13 июня 2019

Я думаю, что вы жертва перехвата параметров в хранимых процессах. Хотите верьте, хотите нет, но это огромная сделка (читайте об этом когда-нибудь), но ее очень легко решить.

Если в вашем запросе @RetailerId, вам нужно только указать локальный параметр в запросе.

declare @_RetailerId  bigint
set @_RetailerId = @RetailerId  -- the one you pass from your stored proc parameters.

Вы можете быть удивлены. План запросов теперь совершенно другой.

0 голосов
/ 10 июня 2019

Я немного переработал первый запрос к этому (для удобства чтения)

SELECT LeadRetailerId, Count(*) as NumberGreenLeads
  FROM  [MBCH_LMT].[lead].[Contact] a
 INNER JOIN [MBCH_LMT].[lead].[ContactActivity] b
         ON b.ContactUID = a.UID
        AND b.LeadStatusDate > DATEADD(Year, -1, GetDate())
 WHERE a.LeadRetailerId in (@RetailerId)
 GROUP BY a.LeadRetailerId

Хотя порядок вещей изменился, MSSQL (скорее всего) выполнит тот же план запроса.Первое, что я заметил, это то, что у вас есть a.LeadRetailerId in (@RetailerId) в запросе.Планируете ли вы добавить дополнительные значения или это только это?Наличие нескольких значений в одной переменной не сработает, как вы могли подумать, что должно быстро показать некоторое простое экспериментирование.Если вы будете использовать только значение 1, вы можете заменить IN на = и упростить запрос до

SELECT LeadRetailerId = @RetailerId, 
       Count(*) as NumberGreenLeads
  FROM [MBCH_LMT].[lead].[Contact] a
 INNER JOIN [MBCH_LMT].[lead].[ContactActivity] b
         ON b.ContactUID = a.UID
        AND b.LeadStatusDate > DATEADD(Year, -1, GetDate())
 WHERE a.LeadRetailerId = @RetailerId

, что может быть немного быстрее.

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

SELECT LeadRetailerId, Count(*) as NumberGreenLeads
  FROM  [MBCH_LMT].[lead].[Contact] a
 INNER JOIN [MBCH_LMT].[lead].[ContactActivity] b
         ON b.ContactUID = a.UID
        AND b.LeadStatusDate > DATEADD(Year, -1, GetDate())
 WHERE a.LeadRetailerId in (@RetailerId1, @RetailerId2, @RetailerId3, @RetailerId4, @RetailerId5, etc)
 GROUP BY a.LeadRetailerId

, где список увеличивается «по мере необходимости».Если это происходит медленно, вы можете проверить, есть ли у вас какие-либо индексы в таблицах, которые могут помочь MSSQL найти быстрый способ обработки этого.В идеале вы должны иметь индекс на a.LeadRetailerId, который включает a.UID для первой таблицы, а другой - для другой таблицы на b.ContactUID и b.LeadStatusDate

CREATE INDEX idx_test ON [MBCH_LMT].[lead].[Contact] (LeadRetailerId) INCLUDE (UID)
CREATE INDEX idx_test ON [MBCH_LMT].[lead].[ContactActivity] (ContactUID, LeadStatusDate)

Имейте в виду, что этоускорит этот запрос, займет дополнительное место в базе данных и создаст некоторые накладные расходы, когда INSERT/UPDATE/DELETE -ing на таблице.(нет такого понятия, как бесплатный обед и т. д., хотя я бы сейчас не слишком волновался об этом, преимущество индексов часто перевешивает накладные расходы ... при разумном использовании =)

Второй запрос немногостранно, что вы делаете GROUP BY b.ContactUID.Я не знаком с вашей структурой таблиц, но вы уверены, что результат указанного запроса - это то, что вы хотите?В любом случае, если это так, то он должен извлечь выгоду из того же предлагаемого индекса в первой таблице.Для третьей таблицы я бы предложил:

CREATE INDEX idx_test ON [MBCH_LMT].[lead].[ContactActivityEscalationHistory] (ContactUID, EscalationLevel)

Одна из вещей, о которой следует помнить при работе с SQL, состоит в том, что система интерпретирует ваш запрос и разработает план выполнения, который она считает наиболее оптимальным.Этот план зависит не только от вашего запроса, но и от наличия индексов, количества и типов данных в таблицах, а иногда и от некоторой черной магии =)

0 голосов
/ 07 июня 2019

Каждый из этих запросов может быть преобразован в индексированные представления с небольшой очисткой (например, COUNT должно стать COUNT_BIG.)

Однако для первого запроса вы не можете иметь выражение GETDATE () и индексировать представление. Для этого вам нужно создать таблицу с именем dbo.LastYear с одной строкой, содержащей предыдущий год; мы назовем это Год . Тогда можете заменить:

AND b.LeadStatusDate > DATEADD(Year, -1, GetDate())

С:

JOIN dbo.LastYear AS ly ON b.LeadStatusDate = ly.Yr

Если эти таблицы находятся в среде с высокими транзакциями, то индексированное представление может не быть вариантом; не забудьте проверить, проверить. Очевидно, что таблицу dbo.LastYear необходимо поддерживать.

...