Скалярная функция вызова производительности в операторе MERGE в SQL Server - PullRequest
0 голосов
/ 05 мая 2019

У меня есть код с оператором MERGE, вызывающим скалярную функцию, и у меня очень низкая производительность.

Этот код может занять у меня 30 минут и более, а иногда он может потерпеть неудачу в середине.

Мой код:

MERGE INTO Table1 AS md
USING (SELECT * FROM Table2 edb
       WHERE edb.FileId = @FileId
         AND edb.IsRowError = 0) edbTable ON dbo.GetId(edbTable.Filed1, edbTable.Filed2, edbTable.Filed3, edbTable.Filed4, edbTable.Filed5, edbTable.Filed6) = md.Id
                                          AND md.IsActive = 1

WHEN MATCHED THEN 
    UPDATE 
        SET .
         .
         .

Функция:

ALTER FUNCTION [dbo].[GetId] 
    (@Filed1 NVARCHAR(9),
     @Filed2 NVARCHAR(9),
     @Filed3 NVARCHAR(13),
     @Filed4 INT,
     @Filed5 DATE,
     @Filed6 INT)
RETURNS INT
AS
BEGIN
    DECLARE @Id INT = NULL

    IF ((@Filed5 IS NULL) OR (@Filed6 = 0))
    BEGIN
        SET @Id = (SELECT TOP 1 md.Id 
                   FROM Table1 md with(NOLOCK)
                   JOIN Table3 mb with(NOLOCK) ON md.Table3Id = mb.Id
                   WHERE (mb.Filed1 = @Filed1 AND mb.Filed2 = @Filed2 
                          OR mb.Filed1 = @Filed2 AND mb.Filed2 = @Filed1)
                     AND mb.Filed7 = 1
                     AND @Filed3 = md.Filed3 
                     AND @Filed4 = md.Filed4 
                   ORDER BY md.LastUpdateDate DESC)

        IF @Id IS NOT NULL
             RETURN @Id 
     ELSE 
         SET @Id = (SELECT TOP 1 md.Id 
                    FROM Table1 md with(NOLOCK)
                    JOIN Table3 mb with(NOLOCK) ON md.Table3Id = mb.Id
                    WHERE (mb.Filed1 = @Filed1 OR mb.Filed1 = @Filed1)
                      AND mb.Filed7 = 1
                      AND @Filed3 = md.Filed3 
                      AND Filed4 = md.Filed4 
                      AND @Filed1 > 0
                    ORDER BY  md.LastUpdateDate DESC)

         IF @Id IS NOT NULL
             RETURN @Id 
         ELSE 
             set @Id =(select top 1 md.Id from Table1 md with(NOLOCK)
                                            join Table3 mb with(NOLOCK) on md.Table3Id = mb.Id
                                            where (mb.Filed1 = @Filed2 
                                                or mb.Filed2 = @Filed2 )
                                              and mb.Filed7 = 1
                                              and @Filed3 = md.Filed3 
                                              and @Filed4 = md.Filed4 
                                              and @Filed2 >0
                                            order by md.LastUpdateDate desc)
 end
 else
 begin
   set @Id = (select top 1 md.Id from Table1 md with(NOLOCK)
                                            join Table3 mb with(NOLOCK) on md.Table3Id = mb.Id
                                            where (mb.Filed1 = @Filed1 and mb.Filed2 = @Filed2 
                                               or mb.Filed1 = @Filed2 and mb.Filed2 = @Filed1 )
                                              and mb.Filed7 = 1
                                              and @Filed3 = md.Filed3 
                                              and @Filed4 = md.Filed4 
                                              and (@Filed5 = md.ContractDate or (@Filed5 is null and md.ContractDate is null))
                                              and @Filed6 = md.PurchasePrice)
   if @Id is not NULL
      RETURN @Id 
   else
      set @Id =(select top 1 md.Id from Table1 md with(NOLOCK)
                                            join Table3 mb with(NOLOCK) on md.Table3Id = mb.Id
                                            where (mb.Filed1 = @Filed1 
                                                or mb.Filed2 = @Filed1 )
                                              and mb.Filed7 = 1
                                              and @Filed3 = md.Filed3 
                                              and @Filed4 = md.Filed4 
                                              and (@Filed5 = md.ContractDate or (@Filed5 is null and md.ContractDate is null))
                                              and @Filed6  = md.PurchasePrice
                                              and @Filed1 >0)
   if @Id is not NULL
      RETURN @Id 
   else
      set @Id =(select top 1 md.Id from Table1 md with(NOLOCK)
                                            join Table3 mb with(NOLOCK) on md.Table3Id = mb.Id
                                            where (mb.Filed1 = @Filed2 
                                                or mb.Filed2 = @Filed2 )
                                              and mb.Filed7 = 1
                                              and @Filed3 = md.Filed3 
                                              and @Filed4 = md.Filed4 
                                              and (@Filed5 = md.ContractDate or (@Filed5 is null and md.ContractDate is null))
                                              and @Filed6 = md.PurchasePrice
                                              and @Filed2 >0)
   if @Id is not NULL
      RETURN @Id 
  end


    RETURN @Id 

END

Что мне нужно изменить? Как я могу изменить код?

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

Если я сделаю это вместо if до join, есть ли способ вытащить первую строку, которая выходит? То есть, если я присоединяюсь, и у первого нет результатов, а у второго, а в третьем, будет возвращен только результат второго, а не третьего - есть ли такая вещь?

1 Ответ

0 голосов
/ 05 мая 2019

Хорошо, это точная копия вашей логики, но как встроенная табличная функция. Скорее всего, это можно урезать дальше, если реквизиты одинаковы. Например, mb.File7 = 1 AND @Filed3 = md.Filed3 AND @Filed4 = md.Filed4 появляется в в каждом предложении WHEN, поэтому его следует переместить из всех предложений WHEN и вставить в WHERE. Это, вероятно, сделает запрос более производительным, а также намного более легким для чтения.

Во всяком случае, это делает ту же логику. Я добавил пару комментариев в код, хотя вы можете посмотреть:

ALTER FUNCTION dbo.GetID (@Filed1 nvarchar(9), --Honestly, give these better names. These parameters don't tell you anything about what they are
                                               --which'll make it harder for people who don't know your function to use it.
                          @Filed2 nvarchar(9),
                          @Filed3 nvarchar(13),
                          @Filed4 int,
                          @Filed5 date,
                          @Filed6 int)
RETURNS TABLE AS RETURN

    SELECT TOP 1 --This may be a bad idea, we'll see
           CASE WHEN @Filed5 IS NULL OR @Filed6 = 0 THEN CASE WHEN (mb.Filed1 = @Filed1 AND mb.Filed2 = @Filed2 
                                                                OR  mb.Filed1 = @Filed2 AND mb.Filed2 = @Filed1)
                                                               AND mb.Filed7 = 1
                                                               AND @Filed3 = md.Filed3 
                                                               AND @Filed4 = md.Filed4 THEN md.Id
                                                              WHEN mb.Filed1 = @Filed1
                                                               AND mb.Filed7 = 1
                                                               AND @Filed3 = md.Filed3 
                                                               AND @Filed4 = md.Filed4 
                                                               AND @Filed1 > 0 THEN md.ID
                                                              WHEN @filed2 IN (mb.Filed1, mb.Filed2)
                                                               AND mb.Filed7 = 1
                                                               AND @Filed3 = md.Filed3 
                                                               AND @Filed4 = md.Filed4 
                                                               AND @Filed2 >0 THEN md.ID
                                                         END
                     --None of these in your original had an ORDER BY clause, was this intentional?
                     --If so, I assume you want a random row, instead. So you'll need a further
                     --CASE expression in your ORDER BY for these, and then the return either NEWID or md.LastUpdateDate
                ELSE CASE WHEN (mb.Filed1 = @Filed1   --Your parenthesis seem wrong here
                           AND  mb.Filed2 = @Filed2   --YOu have an OR in the middle
                            OR  mb.Filed1 = @Filed2   --So I doubt this does what you want
                           AND  mb.Filed2 = @Filed1 ) --But I haven't correct, as I don't know what it should do
                           AND mb.Filed7 = 1
                           AND @Filed3 = md.Filed3 
                           AND @Filed4 = md.Filed4 
                           AND (@Filed5 = md.ContractDate
                            OR  (@Filed5 IS NULL AND md.ContractDate IS))
                           AND @Filed6 = md.PurchasePrice THEN md.ID
                          WHEN (mb.Filed1 = @Filed1 
                            OR  mb.Filed2 = @Filed1)
                           AND mb.Filed7 = 1
                           AND @Filed3 = md.Filed3 
                           AND @Filed4 = md.Filed4 
                           AND (@Filed5 = md.ContractDate
                            OR (@Filed5 IS NULL AND md.ContractDate IS NULL))
                           AND @Filed6  = md.PurchasePrice
                           AND @Filed1 >0 THEN md.Id
                          WHEN (mb.Filed1 = @Filed2 
                            OR  mb.Filed2 = @Filed2)
                           AND mb.Filed7 = 1
                           AND @Filed3 = md.Filed3 
                           AND @Filed4 = md.Filed4 
                           AND (@Filed5 = md.ContractDate
                            OR  (@Filed5 IS NULL AND md.ContractDate IS NULL))
                           AND @Filed6 = md.PurchasePrice
                           AND @Filed2 >0 THEN md.Id
                     END
           END AS Id                         
    FROM dbo.Table1 md --Why md when the table is called "Table1". I assume this is anonymised
         JOIN dbo.Table3 md ON md.Table3Id = mb.Id
    ORDER BY md.LastUpdateDate DESC;
GO
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...