Колегата на MVP Джейми Томсън наскоро посочи, че има грешка с "грешни резултати" в SQL Server, която може да се прояви, когато са изпълнени следните условия:
- Имате индексиран изглед, който свързва поне две таблици;
- тези таблици са ограничени в двете посоки от външен ключ с една колона;
- извършвате актуализации на основната(ите) таблица(и) с помощта на
MERGE
което включва и дветеUPDATE
и (DELETE
илиINSERT
) действия; и, - впоследствие издавате заявки, които препращат към индекса на изгледа (умишлено или не).
За съжаление статията в базата знания, описваща проблема (KB #2756471), е доста лека за подробности. Те не ви казват как да възпроизведете проблема или дори какво по-специално трябва да търсите, за да видите дали това ви засяга; и дори не споменават MERGE
(което всъщност е ядрото на проблема, а не NOEXPAND
, а не проста актуализация). Има някои допълнителни подробности в елемента Connect, които доведоха до корекцията; да се надяваме, че статията в KB ще бъде актуализирана с повече подробности скоро.
Междувременно резултатът, който може да видите, са неправилни данни – или по-добре казано, застояли данни :Заявката може да ви покаже старата версия на актуализирания(ите) ред(ове)! Прекарах няколко минути, опитвайки се да възпроизведа този сценарий в AdventureWorks, и се провалих мизерно. За щастие, Пол Уайт (блог | @SQL_Kiwi) написа изключителна публикация, описваща сценария и показваща пълна репродукция на проблема.
Не мисля, че мога да подчертая колко сериозно е това.
Със сигурност милиони клиенти използват индексирани изгледи, много от тях са мигрирали своя DML код, за да използват MERGE
и голям брой от тях са в Enterprise Edition (или не са, но използват NOEXPAND
намекват или препращат директно към индекса). Пол побърза да посочи, че NOEXPAND
не се изисква за възпроизвеждане на проблема в Enterprise Edition, а също така откри много от другите подробности, необходими за възпроизвеждане на грешката.
Тази публикация не е предназначена да открадне гръмотевици от публикациите на Джейми или Пол; просто опит да се повтори загрижеността и да се повиши осведомеността по този въпрос. Ако имате навика да игнорирате кумулативни актуализации, избирайки да изчакате сервизни пакети, и има някакъв шанс този проблем да ви засегне в момента, вие го дължите на себе си, да не говорим за вашите заинтересовани страни и клиенти, да вземете този въпрос сериозно.
И какво трябва да направите?
Е, какво ще направите по-нататък зависи от това каква версия и издание на SQL Server използвате и дали грешката действително ви засяга (или може).
- Трябва да актуализирате до най-новата Кумулативна актуализация за вашия клон:
Клон Коригирано в CU Изграждане Необходима е минимална компилация
за прилагане на актуализацияКБ Статия
(Изтегляне)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 Таблица 1 :Компилации, които съдържат корекцията
- Ако не приложите корекцията, тогава трябва да тествате всички препратки към вашите изгледи, за да потвърдите, че те връщат правилни резултати във всички случаи – включително след като сте актуализирали базовите таблици с помощта на
MERGE
код> . Ако не го направят (или подозирате, че по-късно може да бъдат засегнати), тогава трябва да изградите отново клъстерирания индекс на всички засегнати изгледи (или да поправите индексирания(и) изглед(и) с помощта наDBCC CHECKTABLE
, както Пол описа в публикацията си), и спрете да използватеMERGE
срещу тези таблици, докато не приложите корекцията. Ако продължите да използватеMERGE
спрямо базовите таблици, подгответе се да продължите да поправяте изгледите, за да избегнете проблема.
- По-бързо решение би било да се предотврати използването на повреден индексиран изглед, като се използва някой от следните необходими методи:
- приложете подсказката за заявката
OPTION (EXPAND VIEWS)
за всички релевантни запитвания; - премахнете всички изрични препратки към индекса в изгледа;
- в стандартни или други издания, където индексираните изгледи не се съпоставят автоматично, премахнете всички екземпляри на
NOEXPAND
.
Но това, разбира се, до голяма степен би погубило целта на индексирания изглед – може и просто да изпусне индекса. Въпреки това, обикновено е по-добре да получите правилните резултати бавно, отколкото бързо да получите грешни резултати; така че може би това е наред.
- приложете подсказката за заявката
SQL Server 2008 SP3
SQL Server 2008 R2 SP1/SP2
SQL Server 2012 RTM/SP1
Вашите опции, ако сте на една от тези компилации:
SQL Server 2008 RTM/SP1/SP2
SQL Server 2008 R2 RTM
За съжаление сте на компилация, която вече не е в основна поддръжка и е малко вероятно този проблем да бъде отстранен вместо вас (освен ако не сте на разширена поддръжка и вдигате много шум). Така че опциите ви са ограничени тук – или преминете към поддържан клон според таблицата по-горе и приложете Кумулативната актуализация, или изберете една от другите опции, споменати по-рано.
SQL Server 2000
SQL Server 2005
Е, лошата новина е, че и вие сте на компилация, която вече не се поддържа. Добрата новина е, че в този конкретен случай това няма значение – не можете да използвате MERGE
така или иначе, така че тази грешка не може да ви засегне.
Други проблеми със MERGE
За съжаление, това далеч не е първата грешка, която сме виждали с MERGE
и вероятно няма да е последното. Ето кратка селекция от десетина MERGE
грешки, които все още са маркирани като активни в Connect:
- #773895 :MERGE неправилно съобщава за уникални нарушения на ключовете
- #766165 :MERGE оценява филтриран индекс на ред, а не след операция, което причинява нарушение на филтриран индекс
- #723696 :Основно MERGE upsert, причиняващо застой
- #713699 :Проверката за потвърждение на системата е неуспешна ("cxrowset.cpp":1528)
- #699055 :плановете за заявки MERGE позволяват FK и CHECK нарушения на ограничения
- #685800 :Параметризирано ИЗТРИВАНЕ и СЛИВАНЕ позволяват нарушения на ограниченията на външния ключ
- #654746 :merge в SQL2008 SP2 все още страда от „Опит за задаване на стойност на колона без NULL на NULL“
- #635778 :NOT MATCHED и MATCHED части от SQL MERGE израз не са оптимизирани
- #633132 :СЛИВАНЕТО ВЪВ С ФИЛИРИРАН ИЗТОЧНИК не работи правилно
- #596086 :Грешка в израза MERGE при използване на INSERT/DELETE и филтриран индекс
- #583719 :Инструкцията MERGE третира неправилно изчислените колони без нула в някои сценарии
- #539084 :MERGE Stmt :Условие за търсене в колона без ключ и ORDER BY в таблицата, извлечена от източника, нарушава MERGE напълно
Сега може да се окаже, че някои от тези грешки действително са отстранени, но състоянието им е неправилно, защото цикълът обратно към Connect не е затворен. Дори и случаят да е такъв, не може да е вярно за всички тях (и потенциално за други, които не съм разкрил).
В допълнение, Дан Гузман демонстрира, че MERGE
не е имунизиран от условия на състезание и други проблеми с паралелността. Заобиколното решение е да използвате HOLDLOCK
(или по-високо ниво на изолация); обаче е често срещано погрешно схващане, че MERGE
е напълно атомен и изобщо не е предразположен към този проблем. Затова ще се чудя на глас:колко MERGE
изявленията там включват HOLDLOCK
(или се изпълняват под SERIALIZABLE
)? Колко са били щателно тествани за проблеми, свързани с едновременността?
Заключение
Лично аз смятам, че синтаксисът е страхотен (макар и обезсърчителен за научаване), но всеки път, когато възникне проблем, това подкопава увереността ми в практичността на замяната на съществуващия DML с новата конструкция.
Имайки това предвид, да не бъда Chicken Little, но не бих се чувствал комфортно да препоръчвам на някой да използва MERGE
освен ако не прилагат изключително изчерпателно тестване. Някои от тези проблеми присъстват и при стандартния UPSERT
методологии, но там проблемите са по-очевидни. MERGE
, само чрез своята същност на едно изявление, ви кара да искате да повярвате в магията. Може би някой ден ще се справи, но точно сега знам, че няма да може да разсече човек наполовина без сериозна помощ.