Fellow MVP Jamie Thomson påpegede for nylig, at der er en "forkerte resultater"-fejl i SQL Server, der kan manifestere sig, når følgende betingelser er sande:
- Du har en indekseret visning, der forbinder mindst to tabeller;
- disse tabeller er begrænset i begge retninger af en enkelt-kolonne fremmed nøgle;
- du udfører opdateringer til basistabellerne ved hjælp af
MERGE
som omfatter bådeOPDATERING
og (SLET
ellerINSERT
) handlinger; og, - du udsteder efterfølgende forespørgsler, der refererer til indekset på visningen (med vilje eller ej).
Desværre er Knowledge Base-artiklen, der beskriver problemet (KB #2756471), ret let på detaljer. De fortæller dig ikke, hvordan du skal genskabe problemet, eller endda hvad du specifikt skal lede efter for at se, om dette påvirker dig; og de nævner ikke engang MERGE
(som faktisk er kernen i problemet, ikke NOEXPAND
, og ikke en simpel opdatering). Der er nogle yderligere detaljer i Connect-elementet, der førte til rettelsen; forhåbentlig vil KB-artiklen blive opdateret med flere detaljer inden længe.
I mellemtiden er resultatet, du kan se, forkerte data – eller bedre sagt, forældede data :Forespørgslen viser dig muligvis den gamle version af de(n) opdaterede række(r)! Jeg brugte et par minutter på at prøve at gengive dette scenarie i AdventureWorks, og det mislykkedes dybt. Heldigvis skrev Paul White (blog | @SQL_Kiwi) et fremragende indlæg, der beskrev scenariet og viste en fuldstændig repro af problemet.
Jeg tror ikke, jeg kan understrege, hvor alvorligt dette er.
Millioner af kunder bruger helt sikkert indekserede visninger, mange af dem har migreret deres DML-kode for at bruge MERGE
, og et stort antal af dem er på Enterprise Edition (eller gør det ikke, men bruger NOEXPAND
tip eller refererer direkte til indekset). Paul var hurtig til at påpege, at NOEXPAND
er ikke påkrævet for at reproducere problemet i Enterprise Edition, og opdagede også mange af de andre detaljer, der kræves for at reproducere fejlen.
Dette indlæg er ikke beregnet til at stjæle nogen torden fra Jamies eller Pauls indlæg; blot et forsøg på at gentage bekymringen og øge bevidstheden om dette spørgsmål. Hvis du har for vane at ignorere kumulative opdateringer, vælge at vente på servicepakker, og der er nogen chance for, at dette problem kan påvirke dig lige nu, skylder du dig selv, for ikke at nævne dine interessenter og kunder, at tage dette problem seriøst.
Så hvad skal du gøre?
Nå, hvad du så gør, afhænger af hvilken version og udgave af SQL Server du kører, og om fejlen faktisk påvirker dig (eller kunne).
- Du bør opdatere til den seneste kumulative opdatering for din filial:
Afdeling Repareret i CU Byg Minimum build påkrævet
for at anvende opdateringKB-artikel
(Download)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 Tabel 1:Builds, der indeholder rettelsen
- Hvis du ikke anvender rettelsen, skal du teste alle referencerne til dine synspunkter for at validere, at de returnerer korrekte resultater i alle tilfælde – også efter at du har opdateret basistabellerne ved hjælp af
MERGE . Hvis de ikke gør det (eller du har mistanke om, at de senere kan blive påvirket), skal du genopbygge det klyngede indeks på alle berørte visninger (eller reparere de indekserede visninger ved hjælp af
DBCC CHECKTABLE
, som Paul har beskrevet i sit indlæg), og stop med at brugeMERGE
mod disse tabeller, indtil du har anvendt rettelsen. Hvis du fortsætter med at brugeMERGE
mod basistabellerne, forbered dig på at fortsætte med at reparere visningerne for at undgå problemet.
- En hurtigere løsning ville være at forhindre den beskadigede indekserede visning i overhovedet at blive brugt, ved at bruge en af følgende metoder:
- anvend forespørgselstippet
MULIGHED (UDVID VISNINGER)
til alle relevante forespørgsler; - fjern eventuelle eksplicitte referencer til indekset på visningen;
- i standardudgaver eller andre udgaver, hvor indekserede visninger ikke matches automatisk, skal du fjerne alle forekomster af
NOEXPAND
.
Men dette ville selvfølgelig i vid udstrækning besejre formålet med den indekserede visning - kan lige så godt bare droppe indekset. Når det er sagt, er det normalt bedre at få de rigtige resultater langsomt, end at få de forkerte resultater hurtigt; så måske er det okay.
- anvend forespørgselstippet
SQL Server 2008 SP3
SQL Server 2008 R2 SP1/SP2
SQL Server 2012 RTM/SP1
Dine muligheder, hvis du er på en af disse builds:
SQL Server 2008 RTM/SP1/SP2
SQL Server 2008 R2 RTM
Desværre er du på en build, der ikke længere er i almindelig support, og det er usandsynligt, at dette problem vil blive løst for dig (medmindre du er på udvidet support, og du laver meget støj). Så dine muligheder er begrænsede her - enten flyt til en understøttet gren i henhold til tabellen ovenfor, og anvend den kumulative opdatering, eller vælg en af de andre muligheder nævnt tidligere.
SQL Server 2000
SQL Server 2005
Nå, den dårlige nyhed er, at du også er på en build, der ikke længere understøttes. Den gode nyhed er, at i dette specifikke tilfælde er det ligegyldigt – du kan ikke bruge MERGE
alligevel, så denne fejl kan ikke påvirke dig.
Andre FLET-problemer
Desværre er dette langt fra den første fejl, vi har set med MERGE
, og det bliver sandsynligvis ikke den sidste. Her er et hurtigt udvalg af et dusin FLET
fejl, der stadig er markeret som aktive på Connect:
- #773895:FLET rapporterer forkerte unikke nøgleovertrædelser
- #766165:FLÉT evaluerer filtreret indeks pr. række, ikke post-operation, hvilket forårsager overtrædelse af filtreret indeks
- #723696:Grundlæggende MERGE-upsert forårsager dødvande
- #713699 :En systempåstandskontrol mislykkedes ("cxrowset.cpp":1528)
- #699055 :FLOT-forespørgselsplaner tillader overtrædelser af FK- og CHECK-begrænsninger
- #685800 :Parametriseret SLET og FLÉT Tillad overtrædelse af begrænsninger for fremmed nøgle
- #654746:fletning i SQL2008 SP2 lider stadig af "Forsøg på at sætte en ikke-NULL-standbar kolonnes værdi til NULL"
- #635778 :NOT MATCHED og MATCHED dele af en SQL MERGE-sætning er ikke optimeret
- #633132:FLOT TIL MED FILTERET KILDE fungerer ikke korrekt
- #596086:MERGE-sætningsfejl, når INSERT/DELETE bruges og filtreret indeks
- #583719 :MERGE-sætning behandler ikke-nulbare beregnede kolonner forkert i nogle scenarier
- #539084 :MERGE Stmt :Søgebetingelse på en ikke-nøglekolonne og en ORDER BY i kildeafledt tabel bryder MERGE fuldstændigt
Nu kan det være tilfældet, at nogle af disse fejl faktisk er blevet rettet, men deres status er forkert, fordi løkken tilbage til Connect ikke er blevet lukket. Selvom det er tilfældet, kan det ikke være sandt for dem alle (og potentielt for andre, som jeg ikke har afsløret).
Derudover er det blevet demonstreret af Dan Guzman, at FLETT
er ikke immun over for raceforhold og andre samtidighedsproblemer. Løsningen er at bruge HOLDLOCK
(eller et højere isolationsniveau); det er dog en almindelig misforståelse, at FLETT
er fuldstændig atomart og slet ikke tilbøjelig til dette problem. Derfor vil jeg undre mig højt:hvor mange MERGE
udsagn derude inkluderer HOLDLOCK
(eller udføres under SERIALIZABLE
)? Hvor mange er blevet grundigt testet for problemer relateret til samtidighed?
Konklusion
Personligt synes jeg, at syntaksen er fantastisk (omend skræmmende at lære), men hver gang et problem dukker op, eroderer det min tillid til det praktiske ved at erstatte eksisterende DML med den nye konstruktion.
Med det i tankerne, ikke for at være Chicken Little, men jeg ville ikke føle mig tryg ved at anbefale nogen at bruge MERGE
medmindre de implementerer ekstremt omfattende test. Nogle af disse problemer er også til stede med standard UPSERT
metoder, men problemerne er mere tydelige der. FLET
, blot på grund af sin enkeltsætningsnatur, giver dig lyst til at tro på magi. Måske en dag vil det levere, men lige nu ved jeg, at det ikke vil være i stand til at save en person i to uden seriøs hjælp.