Kolega MVP Jamie Thomson nedávno poukázal na to, že na serveru SQL Server je chyba „nesprávných výsledků“, která se může projevit, když jsou splněny následující podmínky:
- Máte indexované zobrazení, které spojuje alespoň dvě tabulky;
- tyto tabulky jsou omezeny v obou směrech cizím klíčem s jedním sloupcem;
- aktualizace základních tabulek provádíte pomocí
MERGE
který zahrnuje jakUPDATE
a (DELETE
neboINSERT
) akce; a - následně zadáte dotazy, které odkazují na index v zobrazení (záměrně nebo ne).
Bohužel článek znalostní báze popisující problém (KB č. 2756471) je v podrobnostech poměrně stručný. Neřeknou vám, jak problém reprodukovat, nebo dokonce ani to, co konkrétně byste měli hledat, abyste zjistili, zda se vás to týká; a ani se nezmiňují o MERGE
(což je ve skutečnosti jádro problému, nikoli NOEXPAND
a nejde o jednoduchou aktualizaci). V položce Connect jsou některé další podrobnosti, které přinesly opravu; doufám, že článek KB bude brzy aktualizován o další podrobnosti.
Mezitím mohou být výsledkem nesprávná data – nebo lépe řečeno zastaralá data :Dotaz vám může zobrazit starou verzi aktualizovaného řádku (řádků)! Strávil jsem několik minut pokusem o reprodukování tohoto scénáře v AdventureWorks a velmi selhal. Naštěstí Paul White (blog | @SQL_Kiwi) napsal vynikající příspěvek popisující scénář a ukazující úplné opakování problému.
Nemyslím si, že mohu zdůraznit, jak vážné to je.
Miliony zákazníků jistě používají indexovaná zobrazení, mnozí z nich migrovali svůj kód DML na MERGE
a velký počet z nich je na Enterprise Edition (nebo nejsou, ale používají NOEXPAND
nápověda nebo přímo odkazují na index). Paul rychle poukázal na to, že NOEXPAND
není vyžadována k reprodukci problému v Enterprise Edition a také objevil mnoho dalších podrobností potřebných k reprodukci chyby.
Účelem tohoto příspěvku není ukrást jakýkoli hrom z příspěvků Jamieho nebo Paula; jen pokus zopakovat obavy a zvýšit povědomí o tomto problému. Pokud máte ve zvyku ignorovat kumulativní aktualizace, rozhodnete se čekat na aktualizace Service Pack a existuje nějaká šance, že by se vás tento problém mohl týkat právě teď, dlužíte sami sobě, nemluvě o vašich akcionářích a zákaznících, abyste tento problém vážně.
Co byste tedy měli dělat?
Co uděláte dále, závisí na tom, jakou verzi a edici SQL Serveru používáte, a zda se vás chyba skutečně týká (nebo by mohla).
- Měli byste aktualizovat na nejnovější kumulativní aktualizaci pro vaši pobočku:
Pobočka Opraveno v CU Sestavit Požaduje se minimální sestavení
k použití aktualizaceČlánek KB
(Stáhnout)2008 Service Pack 3 CU #8 10.00.5828 10 00 5500 KB #2771833 2008 R2 Service Pack 1 CU #10 10.50.2868 10.50.2500 KB #2783135 2008 R2 Service Pack 2 CU #4 10.50.4270 10 00 4000 KB #2777358 2012 RTM CU #5 11.00.2395 11.00.2100 KB #2777772 2012 Service Pack 1 CU #2 11.00.3339 11.00.3000 KB #2790947 Tabulka 1:Sestavení obsahující opravu
- Pokud opravu nepoužijete, musíte otestovat všechny odkazy na vaše zobrazení, abyste ověřili, že ve všech případech vracejí správné výsledky – i poté, co jste aktualizovali základní tabulky pomocí
MERGE
kód> . Pokud tomu tak není (nebo máte podezření, že by mohly být později ovlivněny), měli byste znovu sestavit seskupený index na všech dotčených pohledech (nebo opravit indexované pohledy pomocíDBCC CHECKTABLE
, jak Paul popsal ve svém příspěvku), a přestaňte používatMERGE
proti těmto tabulkám, dokud nepoužijete opravu. Pokud budete pokračovat v používáníMERGE
v porovnání se základními tabulkami se připravte na pokračování opravy pohledů, abyste se vyhnuli problému.
- Rychlejší opravou by bylo zabránit tomu, aby se poškozený indexovaný pohled vůbec používal, a to pomocí kterékoli z následujících požadovaných metod:
- použijte nápovědu k dotazu
OPTION (EXPAND VIEWS)
na všechny relevantní dotazy; - odstraňte ze zobrazení veškeré explicitní odkazy na index;
- ve standardních nebo jiných edicích, kde se indexovaná zobrazení automaticky neshodují, odeberte všechny výskyty
NOEXPAND
.
Ale to by samozřejmě do značné míry zmařilo účel indexovaného pohledu – může to také jednoduše index vypustit. To znamená, že je obvykle lepší získat správné výsledky pomalu, než rychle získat špatné výsledky; takže možná je to v pořádku.
- použijte nápovědu k dotazu
SQL Server 2008 SP3
SQL Server 2008 R2 SP1/SP2
SQL Server 2012 RTM/SP1
Vaše možnosti, pokud používáte jednu z těchto sestav:
SQL Server 2008 RTM/SP1/SP2
SQL Server 2008 R2 RTM
Bohužel jste na sestavení, které již není v běžné podpoře, a je nepravděpodobné, že tento problém bude vyřešen za vás (pokud nemáte rozšířenou podporu a neděláte spoustu hluku). Vaše možnosti jsou zde tedy omezené – buď se přesuňte do podporované větve podle výše uvedené tabulky a použijte kumulativní aktualizaci, nebo zvolte jednu z dalších možností uvedených výše.
SQL Server 2000
SQL Server 2005
Špatná zpráva je, že jste také na sestavení, které již není podporováno. Dobrou zprávou je, že v tomto konkrétním případě na tom nezáleží – nemůžete použít MERGE
každopádně vás tato chyba nemůže ovlivnit.
Další problémy MERGE
Bohužel to není zdaleka první chyba, kterou jsme viděli u MERGE
a pravděpodobně nebude poslední. Zde je rychlý výběr z tuctu MERGE
chyby, které jsou na Connectu stále označeny jako aktivní:
- #773895 :SLOUČENÍ nesprávně hlásí jedinečná porušení klíčových slov
- #766165 :MERGE vyhodnocuje filtrovaný index na řádek, nikoli po operaci, což způsobuje porušení filtrovaného indexu
- #723696:Základní MERGE upsert způsobující uváznutí
- #713699 :Kontrola systémového tvrzení selhala ("cxrowset.cpp":1528)
- #699055 :Plány dotazů MERGE umožňují porušení omezení FK a CHECK
- #685800 :Parametrizované DELETE a MERGE umožňují porušení omezení cizího klíče
- #654746 :sloučení v SQL2008 SP2 stále trpí "Pokusem nastavit hodnotu sloupce, který nemá hodnotu NULL" na NULL"
- #635778 :Části NOT MATCHED a MATCHED příkazu SQL MERGE nejsou optimalizovány
- #633132 :SLOUČENÍ DO S FILTROVANÝM ZDROJEM nefunguje správně
- #596086 :Chyba příkazu MERGE při použití a filtrování indexu INSERT/DELETE
- #583719 :Příkaz MERGE v některých scénářích nesprávně zachází s vypočítanými sloupci bez možnosti null
- #539084 :MERGE Stmt :Podmínka vyhledávání v neklíčovém sloupci a ORDER BY ve zdrojové odvozené tabulce zcela přeruší MERGE
Nyní se může stát, že některé z těchto chyb byly skutečně opraveny, ale jejich stav je nesprávný, protože smyčka zpět do Connect nebyla uzavřena. I kdyby tomu tak bylo, nemůže to být pravda pro všechny (a potenciálně pro další, které jsem neodhalil).
Dan Guzman navíc prokázal, že MERGE
není imunní vůči rasovým podmínkám a dalším problémům souběžnosti. Řešením je použití HOLDLOCK
(nebo vyšší úroveň izolace); nicméně, to je obyčejná mylná představa, že MERGE
je zcela atomový a není k tomuto problému vůbec náchylný. Proto se nahlas ptám:kolik MERGE
příkazy tam zahrnují HOLDLOCK
(nebo jsou spouštěny pod SERIALIZABLE
)? Kolik z nich bylo důkladně testováno na problémy související se souběžností?
Závěr
Osobně si myslím, že syntaxe je skvělá (ačkoli skličující se naučit), ale pokaždé, když se objeví nějaký problém, nahlodá mou důvěru v praktičnost nahrazení stávajícího DML novým konstruktem.
S ohledem na to nebýt Chicken Little, ale necítil bych se pohodlně doporučovat komukoli, aby používal MERGE
pokud nezavedou extrémně komplexní testování. Některé z těchto problémů se vyskytují také u standardního UPSERT
metodiky, ale tam jsou problémy zjevnější. MERGE
, pouze díky své povaze s jedním výrokem vás nutí věřit v magii. Možná to jednoho dne přinese, ale právě teď vím, že bez seriózní pomoci nebude schopné rozpůlit člověka.