Помогите улучшить соединение SQL - PullRequest
0 голосов
/ 09 февраля 2011

У меня есть хранимая процедура, которая выполняется для обновления игровых очков для баланса пользователя. Это вставка с 5 подзапросами. Я выделил один из подзапросов как запрос, который замедляет весь пакет. Без этого хранимая процедура будет выполняться менее чем за 2 секунды. С ним это займет целых 8 секунд. 8 секунд - не конец света, но ради масштабируемости мне нужно будет завершить его быстрее. Вот изолированный подзапрос:

(SELECT IsNull(Sum(A.TransAmount) + Sum(Case When A.BetResult = 1 Then (A.BetWinAmount + (A.TransAmount * -1)) End), 0)
            FROM User_T A
            LEFT OUTER JOIN User_TD B on A.TID = B.TID
            LEFT OUTER JOIN Lines_BL C ON B.LID = C.LID
            LEFT OUTER JOIN Lines_BM D ON C.BMID = D.BMID
            LEFT OUTER JOIN Event_M E ON D.EID = E.EID
            LEFT OUTER JOIN Event_KB F ON A.TransReason = F.BID
            LEFT OUTER JOIN Event_M G ON F.BID = G.EID
        where A.UserID = U.UserID AND (A.IsSettled = 1)
        AND 
        (
        (A.TransReason = 1 AND (datediff(dd, Convert(datetime, E.EDate, 101), Convert(datetime, @EndDate, 101)) = @DaysAgo)) OR 
        (A.TransReason >= 3000 AND (datediff(dd, Convert(datetime, G.EDate, 101), Convert(datetime, @EndDate, 101)) = @DaysAgo)
                AND  [dbo].[Event_CEAFKBID](A.TransReason) = 1) OR
        (A.TransReason BETWEEN 3 and 150 AND (datediff(dd, Convert(datetime, A.TransDT, 101), Convert(datetime, @EndDate, 101)) = @DaysAgo))
        )

Что я сделал для дальнейшей изоляции: когда я запускаю Select * только для объединений (без предложений where), производительность очень хорошая -> 100 000 строк в секунду. Как я добавляю в предложения where, я считаю, что значительное замедление связано с предложением 'или' и / или функцией, которую необходимо оценить.

Насколько я понимаю, функция внутри предложения where оценивает каждую строку - в отличие от какого-либо кэширования определения функции и оценки таким образом. У меня есть индексы в таблицах, но мне интересно, если некоторые из них не являются правильными.

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

Ответы [ 5 ]

2 голосов
/ 09 февраля 2011

Я подозреваю, что ваши самые большие потери производительности связаны с коррелированным подзапросом (вне зависимости от таблицы U.UserId ) и вызовом встроенной функции dbo.Event_CEAFKBID. Многое, конечно, зависит от того, насколько большие таблицы (сколько строк читается). Все эти преобразования даты и времени не помогут и не создадут очень сильный запах «плохого дизайна», но я не думаю, что они слишком сильно повлияют на производительность.

Эти левые внешние объединения ужасны, так как оптимизатор должен проверять их все на наличие строк - поэтому, если «A» большое, все объединения на все строки должны будет выполнено, даже если там нет данных. Если они могут быть заменены внутренними объединениями, сделайте это, но я предполагаю, что не из-за этой логики «таблица E или таблица G». Lesses, это выглядит так, как будто вы получили три отдельных запроса, соединенных в один; если бы вы разбили это на три, объединившись вместе, это выглядело бы как запрос Франкенштейна ниже Я не знаю, будет ли это работать быстрее или нет (черт возьми, я даже не могу отладить запрос и убедиться в сбалансированности панетез), но если у вас есть разреженные данные относительно вашей логики, это должно выполняться довольно быстро. (Я сделал преобразование даты, чтобы сделать код более разборчивым, вам нужно подключить их обратно.)

SELECT isnull(sum(Total), 0) FinalTotal from (
SELECT 
   sum(A.TransAmount + Case When A.BetResult = 1 Then A.BetWinAmount - A.TransAmount else 0 End) Total
 FROM User_T A            
 INNER JOIN User_TD B on A.TID = B.TID             
INNER JOIN Lines_BL C ON B.LID = C.LID             
INNER JOIN Lines_BM D ON C.BMID = D.BMID             
INNER JOIN Event_M E ON D.EID = E.EID             
 where A.UserID = U.UserID
  AND A.IsSettled = 1
  AND A.TransReason = 1 
  AND (datediff(dd, E.EDate, @EndDate) = @DaysAgo)) 

UNION ALL SELECT 
   sum(A.TransAmount + Case When A.BetResult = 1 Then A.BetWinAmount - A.TransAmount else 0 End) Total
 FROM User_T A            
INNER JOIN Event_KB F ON A.TransReason = F.BID             
INNER JOIN Event_M G ON F.BID = G.EID        
 where A.UserID = U.UserID
  AND A.IsSettled = 1
  AND A.TransReason >= 3000 
  AND (datediff(dd, G.EDate, @EndDate) = @DaysAgo)                 
  AND [dbo].[Event_CEAFKBID](A.TransReason) = 1

UNION ALL SELECT 
   sum(A.TransAmount + Case When A.BetResult = 1 Then A.BetWinAmount - A.TransAmount else 0 End) Total
 FROM User_T A            
 where A.UserID = U.UserID
  AND A.IsSettled = 1
  AND A.TransReason BETWEEN 3 and 150 
  AND datediff(dd, A.TransDT, @EndDate) = @DaysAgo)
) ThreeWayUnion
1 голос
/ 09 февраля 2011

Вы должны проверить, нужны ли внешние объединения - они дороже внутренних соединений. У вас есть некоторые значения, взятые из доминантной таблицы, с тегом A. У вас также есть условие OR, которое ссылается на E, и условие OR, которое ссылается на G. Я бы посмотрел, чтобы реструктурировать запрос следующим образом:

SELECT SUM(x.result)
  FROM (SELECT A.TransAmount + CASE WHEN A.BetResult = 1
                               THEN (A.BetWinAmount + (A.TransAmount * -1))
                               ELSE 0 END AS result
          FROM A
         WHERE A.TransReason BETWEEN 3 AND 150
           AND datediff(dd, Convert(datetime, A.TransDT, 101),
                            Convert(datetime, @EndDate,  101)) = @DaysAgo
           AND A.UserID = U.UserID    -- Where does alias U come from?
           AND A.IsSettled = 1
        UNION
        SELECT A.TransAmount + CASE WHEN A.BetResult = 1
                               THEN (A.BetWinAmount + (A.TransAmount * -1))
                               ELSE 0 END AS result
          FROM User_T   A
          JOIN User_TD  B ON A.TID  = B.TID
          JOIN Lines_BL C ON B.LID  = C.LID
          JOIN Lines_BM D ON C.BMID = D.BMID
          JOIN Event_M  E ON D.EID  = E.EID
         WHERE A.TransReason = 1
           AND datediff(dd, Convert(datetime, E.EDate,  101),
                            Convert(datetime, @EndDate, 101)) = @DaysAgo
           AND A.UserID = U.UserID    -- Where does alias U come from?
           AND A.IsSettled = 1
        UNION
        SELECT A.TransAmount + CASE WHEN A.BetResult = 1
                               THEN (A.BetWinAmount + (A.TransAmount * -1))
                               ELSE 0 END S result
          FROM User_T   A
          JOIN User_TD  B ON A.TID  = B.TID
          JOIN Lines_BL C ON B.LID  = C.LID
          JOIN Lines_BM D ON C.BMID = D.BMID
          JOIN Event_M  E ON D.EID  = E.EID
          JOIN Event_KB F ON A.TransReason = F.BID
          JOIN Event_M  G ON F.BID  = G.EID
         WHERE A.TransReason >= 3000
           AND datediff(dd, Convert(datetime, G.EDate, 101),
                            Convert(datetime, @EndDate, 101)) = @DaysAgo
           AND [dbo].[Event_CEAFKBID](A.TransReason) = 1
           AND A.UserID = U.UserID    -- Where does alias U come from?
           AND A.IsSettled = 1
       ) AS x

Мысль здесь заключается в том, что запросы на внутреннее объединение будут выполняться быстрее, чем запросы на внешнее объединение, и суммирование промежуточных результатов не является затруднительным для СУБД (так оно и было). Это, вероятно, также устраняет необходимость в IFNULL.

Псевдоним U, по-видимому, является ссылкой на внешний запрос, частью которого он является.

1 голос
/ 09 февраля 2011

Коррелированные подзапросы - очень плохая методика программирования, которая приравнивается к использованию курсора в запросе. Вместо этого сделайте его производной таблицей.

И да, эти функции замедляют вас. Если вам нужно преобразовать в datetime, структуру вашей базы данных необходимо исправить, а данные правильно хранить как datetime.

1 голос
/ 09 февраля 2011

Вам нужно выполнить преобразования в дату / время для функций DATEDIFF? Вы сохраняете даты в качестве теста, или вы реконвертируете, чтобы избавиться от времени? Если да, то вам не нужно, так как дни будут правильными, включая время.

1 голос
/ 09 февраля 2011

Вы можете поместить дело в причину, а не в первую строку выбора. зачем вам ставить много объединений, если в этом утверждении вы просто используете таблицы A, E и G?

Для повышения эффективности запросов вы можете использовать план выполнения в Management Studio.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...