Слияние SQL - как я могу это оптимизировать?

Таблица A (таблица для слияния) имеет 90000 строк. Таблица B (исходная таблица) содержит 3677 строк, я ожидал бы, что это слияние произойдет очень быстро, но это займет 30 минут (и продолжает подсчет). Как его можно оптимизировать, чтобы он работал быстрее?

ALTER PROCEDURE [dbo].[MergeAddressFromGraph] 
-- no params

AS BEGIN - добавлен SET NOCOUNT ON, чтобы дополнительные наборы результатов - не мешали операторам SELECT. УСТАНОВИТЬ NOCOUNT ON;

-- first add fids to the MergeFundraiserNameAddress table instead of the temp table?

SELECT fundraiserid, ein
INTO #fids 
FROM  bb02_fundraiser

BEGIN TRAN;
MERGE BB02_FundraiserNameAddress AS T
USING
(    
    select f.fundraiserid,
           n.addresslines,
           n.town,
           n.county,
           n.postcode,
           n.country,
           n.fulladdress,
           n.ein
    from MergeFundraiserNameAddress n
         join bb02_fundraiser f
         on f.ein = n.ein and f.isdefault = 1
    group by n.ein,
             f.fundraiserid,
             n.addresslines,
             n.town,
             n.county,
             n.postcode,
             n.country,
             n.fulladdress

) AS S
ON (T.fundraiserid in( (select fundraiserid from #fids where ein = S.ein)) )

WHEN MATCHED
    THEN UPDATE
        SET    
              -- ADDRESS
              T.addresslines = S.addresslines
              ,T.town = S.town
              ,T.county = S.county
              ,T.postcode = S.postcode
              ,T.country = S.country
              ,T.fulladdress = S.fulladdress

;

DELETE FROM MergeFundraiserNameAddress

COMMIT TRAN;

drop table #fids

КОНЕЦ

ОБНОВЛЕНИЕ. Мне удалось улучшить хранимую процедуру, которая теперь выполняется всего за несколько секунд. Я присоединился к таблице temp вместо таблицы bb02_fundraiser и удалил подзапрос в предложении ON.

Теперь я понимаю, что в слиянии нет необходимости, и я мог бы использовать вместо него Update, но сейчас меня это устраивает, потому что вскоре может потребоваться INSERT при рефакторинге.

ОБНОВЛЕННАЯ СОХРАНЕННАЯ ПРОЦЕДУРА НИЖЕ ЕСЛИ OBJECT_ID ('tempdb .. # fids') НЕ ЯВЛЯЕТСЯ NULL DROP TABLE #fids

SELECT fundraiserid, ein
INTO #fids 
FROM  bb02_fundraiser
where isdefault = 1

BEGIN TRAN;
MERGE BB02_FundraiserNameAddress AS T
USING
(    
    select f.fundraiserid,
           n.addresslines,
           n.town,
           n.county,
           n.postcode,
           n.country,
           n.fulladdress,
           n.ein
    from MergeFundraiserNameAddress n
         join #fids f
         on f.ein = n.ein
    group by n.ein,
             f.fundraiserid,
             n.addresslines,
             n.town,
             n.county,
             n.postcode,
             n.country,
             n.fulladdress

) AS S
ON (T.fundraiserid = S.fundraiserid)

WHEN MATCHED
    THEN UPDATE
        SET    
              -- ADDRESS
              T.addresslines = S.addresslines
              ,T.town = S.town
              ,T.county = S.county
              ,T.postcode = S.postcode
              ,T.country = S.country
              ,T.fulladdress = S.fulladdress

;

DELETE FROM MergeFundraiserNameAddress

COMMIT TRAN;

IF OBJECT_ID('tempdb..#fids') IS NOT NULL
DROP TABLE #fids

person Induster    schedule 12.12.2013    source источник
comment
Пожалуйста, опубликуйте план выполнения. Вы правы - мы можем сделать это очень быстро.   -  person usr    schedule 13.12.2013
comment
Почему вы используете MERGE, если единственный возможный результат - ОБНОВЛЕНИЕ? (Вот почему вы можете проявить осторожность с MERGE.)   -  person Aaron Bertrand    schedule 13.12.2013
comment
Также, возможно, он заблокирован. Что говорит sys.dm_exec_requests, когда он работает в течение 30 минут? Это DMV может показать вам blocking_session_id, а также тип ожидания для session_id, пытающегося запустить MERGE.   -  person Aaron Bertrand    schedule 13.12.2013


Ответы (2)


См. Ниже, если только это утверждение работает за вас.

UPDATE T
SET    T.addresslines = n.addresslines
      ,T.town = n.town
      ,T.county = n.county
      ,T.postcode = n.postcode
      ,T.country = n.country
      ,T.fulladdress = n.fulladdress
    from MergeFundraiserNameAddress n join bb02_fundraiser f
    on f.ein = n.ein and f.isdefault = 1
    INNER JOIN  BB02_FundraiserNameAddress T
    ON T.fundraiserid = f.fundraiserid AND T.ein = f.ein
group by n.ein,
             f.fundraiserid,
             n.addresslines,
             n.town,
             n.county,
             n.postcode,
             n.country,
             n.fulladdress

Как упоминали другие пользователи в ваших комментариях, зачем использовать оператор MERGE, когда вы только обновляете записи. Оператор MERGE используется, когда вы выполняете несколько операций, таких как UPDATE, DELETE и INSERT.

Поскольку вы только ОБНОВЛЯЕТЕ записи, нет необходимости в операторе слияния.

Причина низкой производительности

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

Когда вы выполняете SELECT * INTO #TempTable FROM Some_Table, он переносит все данные из Some_Table в таблицу Temp, но не индексы. вы можете увидеть себя, выполнив этот простой запрос

select * from tempdb.sys.indexes
where object_id = (select object_id 
                   from tempdb.sys.objects 
                   where name LIKE '#TempTable%')
person M.Ali    schedule 12.12.2013
comment
Хорошее предложение, но индекс в таблице из 3000 строк не будет иметь никакого значения - person Nick.McDermaid; 13.12.2013
comment
@ElectricLlama True, но если OP жалуется на производительность, самым первым подозреваемым будет эта таблица Temp в его коде. - person M.Ali; 13.12.2013
comment
Я не согласен, у нас есть таблица с 90 000 строками, которая обновляется из таблицы с 3677 строками, я подозреваю, что это таблица с 90 000 строками. Подозрение можно было превратить в уверенность, наблюдая за планом запроса, операциями ввода-вывода и т. Д. - person Nick.McDermaid; 13.12.2013
comment
@ElectricLlama Я согласен с вами, но почему вы непреклонны в том, что пользователь будет иметь около 3000 строк в своей временной таблице. Очевидно, что в случае медленного запроса кто-нибудь увидит план выполнения 1-й, прежде чем создавать индексы или что-то еще, но в этом конкретном случае, когда план выполнения не предоставляется, я только догадываюсь, и мое предположение так же хорошо, как и ваше, поскольку ни у вас, ни у меня нет любое количество строк или план выполнения для этого запроса :) - person M.Ali; 13.12.2013
comment
Он упомянул количество строк в своих комментариях. Конечно, количество строк может измениться, и в этом случае индекс определенно имеет смысл. - person Nick.McDermaid; 13.12.2013
comment
@ElectricLlama lol opsss только что увидел количество строк, спасибо, что указали на это :) - person M.Ali; 13.12.2013
comment
@ElectricLlama да, вероятно, с голосованием против lol, но я сильно упростил запрос на обновление, поэтому, надеюсь, OP увидит некоторое улучшение производительности. - person M.Ali; 13.12.2013
comment
Да, безусловно, вы помогли, переписав это для него. - person Nick.McDermaid; 13.12.2013
comment
Приносим извинения за поздний ответ. Был напряженный день мягко говоря. Хотя я не тестировал вашу перезапись, просто взглянув на нее, я понял, что она работает. Единственное изменение, которое мне нужно сделать, это присоединиться к временной таблице (#fids) вместо bb02_fundraiser. Однако я пока придерживаюсь слияния, потому что мне удалось заставить его работать (см. Мое обновление выше). - person Induster; 13.12.2013

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

усечь таблицу MergeFundraiserNameAddress

person KumarHarsh    schedule 13.12.2013
comment
Я не эксперт по SQL, поэтому я еще многое не знаю. Спасибо за предложение. Я посмотрел на усечение и удаление и понял, что вы имеете в виду. stackoverflow.com/questions/3256242 / - person Induster; 13.12.2013