sql >> Databáze >  >> RDS >> Sqlserver

T-SQL MERGE Výkon v typickém publikačním kontextu

Náklady na podstrom by se měly brát s velkou rezervou (a zvláště když máte velké chyby mohutnosti). SET STATISTICS IO ON; SET STATISTICS TIME ON; výstup je lepším ukazatelem skutečného výkonu.

Řazení s nulovým řádkem nezabere 87 % zdrojů. Tento problém ve vašem plánu je jedním ze statistických odhadů. Náklady uvedené ve skutečném plánu jsou stále odhadované náklady. Neupravuje je, aby zohlednily, co se skutečně stalo.

V plánu je bod, kdy filtr sníží 1 911 721 řádků na 0, ale odhadovaný počet řádků vpřed je 1 860 310. Poté jsou všechny náklady falešné a vyvrcholí 87% odhadovanými náklady na 3 348 560 řádků.

Chybu odhadu mohutnosti lze reprodukovat mimo Merge pohledem na odhadovaný plán pro Full Outer Join s ekvivalentními predikáty (poskytuje stejný odhad 1 860 310 řádků).

SELECT * 
FROM TargetTable T
FULL OUTER JOIN  @tSource S
    ON S.Key1 = T.Key1 and S.Key2 = T.Key2
WHERE 
CASE WHEN S.Key1 IS NOT NULL 
     /*Matched by Source*/
     THEN CASE WHEN T.Key1 IS NOT NULL  
               /*Matched by Target*/
               THEN CASE WHEN  [T].[Data1]<>S.[Data1] OR 
                               [T].[Data2]<>S.[Data2] OR 
                               [T].[Data3]<>S.[Data3]
                         THEN (1) 
                     END 
                /*Not Matched by Target*/     
                ELSE (4) 
           END 
       /*Not Matched by Source*/     
      ELSE CASE WHEN  [T].[Key1][email protected] 
                THEN (3) 
            END 
END IS NOT NULL

To znamená, že plán až po samotný filtr vypadá docela neoptimálně. Provádí úplné skenování indexu clusteru, když možná chcete plán se 2 hledáními rozsahu clusteru. Jeden pro načtení jednoho řádku shodného s primárním klíčem ze spojení na zdroji a druhý pro načtení T.Key1 = @id rozsah (i když je to možná proto, aby se později nemuselo třídit do seskupeného klíče?)

Možná byste mohli zkusit toto přepsání a zjistit, zda to funguje lépe nebo hůře

;WITH FilteredTarget AS
(
SELECT T.*
FROM TargetTable  AS T WITH (FORCESEEK)
JOIN @tSource S
    ON (T.Key1 = S.Key1
    AND S.Key2 = T.Key2)
    OR T.Key1 = @id
)
MERGE FilteredTarget AS T
USING @tSource S
ON (T.Key1 = S.Key1
   AND S.Key2 = T.Key2)


-- Only update if the Data columns do not match
WHEN MATCHED AND S.Key1 = T.Key1 AND S.Key2 = T.Key2 AND 
                                         (T.Data1 <> S.Data1 OR
                                          T.Data2 <> S.Data2 OR 
                                          T.Data3 <> S.Data3) THEN
  UPDATE SET T.Data1 = S.Data1,
             T.Data2 = S.Data2,
             T.Data3 = S.Data3

-- Note from original poster: This extra "safety clause" turned out not to
-- affect the behavior or the execution plan, so I removed it and it works
-- just as well without, but if you find yourself in a similar situation
-- you might want to give it a try.
-- WHEN MATCHED AND (S.Key1 <> T.Key1 OR S.Key2 <> T.Key2) AND T.Key1 = @id THEN
--   DELETE

-- Insert when missing in the target
WHEN NOT MATCHED BY TARGET THEN
    INSERT (Key1, Key2, Data1, Data2, Data3)
    VALUES (Key1, Key2, Data1, Data2, Data3)

WHEN NOT MATCHED BY SOURCE AND T.Key1 = @id THEN
    DELETE;



  1. Chyba dotazování KGXGN (15)

  2. Komentáře k problému s návrhem databáze mnoha tabulek

  3. mysqldump:Při zápisu došlo k chybě 32

  4. Převést unixový čas na UTC datetime