Műszaki cikk

Excel képletgyorsítótárak auditálása HotXLS deep recalccal

A HotXLS megválaszolja azt a kérdést, amelyet minden táblázatfolyamat előbb-utóbb fel kell tegyen: egyeznek-e még a munkafüzetben tárolt számok azokkal a képletekkel, amelyek előállították őket. A CalculateAndVerify a teljes függőségi gráfot elkülönített overlaybe számolja újra, minden eredményt összehasonlít a cellában már lévő gyorsítótárazott értékkel, és jelenti az eltéréseket. Alapértelmezés szerint semmit sem változtat

Az, hogy ez fontos, abból ered, hogy egy táblázatfájl képletcellánként két dolgot tárol: a képletet és az utolsó értéket, amelyet valaki számára kiszámolt. Az Excel szinkronban tartja őket. A világ minden mása nem feltétlenül. Egy olyan fájl, amely átesett egy régebbi könyvtáron, egy részleges újraszámoláson, egy kézzel szerkesztett XML részen vagy egy olyan eszközön, amely értékeket írt újraszámolás nélkül, készségesen mutat olyan összeget, amely már nem következik a bemeneteiből, és a fájlformátumban semmi sem jelzi ezt

Miért ilyen veszélyes egy a képletével ellentmondó gyorsítótárazott érték?

Mert minden hétköznapi olvasási úton láthatatlan. Nyissa meg a fájlt egy megjelenítőben, olvassa a cellát API-n keresztül, exportálja CSV-be vagy PDF-be, és a gyorsítótárazott számot kapja. A képlet ott van ugyanabban a cellában, és senki nem hasonlítja őket össze. Az eltérés csak akkor bukkan fel, amikor valaki megnyitja a munkafüzetet Excelben, amely a legtöbb beállítás mellett betöltéskor újraszámol, és hirtelen egy tavaly negyedévben lezárt jelentés más összegeket mutat

Az audit azért létezik, hogy ezt az összehasonlítást tudatos, ütemezett műveletté tegye, nem véletlen ütközetté. Ez a táblázat megfelelője annak, amikor ellenőrzőösszeget verifikálnak: elég olcsó ahhoz, hogy egy beérkező folyamatsorban fussan, és az egyetlen dolog, amely egy csendes adatintegritási problémából cselekvhető jelentést csinál

var
  Book: TXLSWorkbook;
  Options: TXLSRecalcAuditOptions;
  Report: TXLSCalculationAuditReport;
  I: Integer;
begin
  Book := TXLSWorkbook.Create(nil);
  try
    Book.LoadFromFile('quarterly-close.xls');
    Options := TXLSRecalcAuditOptions.Default;
    Options.MaxIssues := 500;
    Report := Book.CalculateAndVerify(Options);
    try
      for I := 0 to Report.Count - 1 do
        if Report[I].Kind = xlcaiCacheMismatch then
          Writeln(Report[I].SheetName, '!',
                  Report[I].Row, ':', Report[I].Col, '  ',
                  Report[I].Formula,
                  '  cached=', VarToStr(Report[I].Actual),
                  '  recomputed=', VarToStr(Report[I].Expected));
      if Report.Truncated then
        Writeln('issue budget reached, raise MaxIssues');
    finally
      Report.Free;
    end;
  finally
    Book.Free;
  end;
end;

Három túlterhelés létezik, és három különböző kérdésre válaszolnak. A paraméternélküli CalculateAndVerify eltérésszámot ad vissza, ami mind, amire egy egészségellenőrzésnek szüksége van. Az eltérések out tömbjével bíró túlterhelés megadja a cellákat. A TXLSRecalcAuditOptions-t fogadó túlterhelés teljes TXLSCalculationAuditReport-ot ad vissza, ez az, amelyhez akkor nyúljon, amikor nemcsak azt kell tudnia, hogy egy érték eltér, hanem azt is, miért nem tudott az audit valamit kiértékelni

Az overlay, és miért nem ír az audit

Minden újraszámolt érték overlaybe kerül, a cellagyorsítótárba nem, és az overlay mindkét munkafüzetmotorban a cellaolvasási callback legelső elejébe injektálódik. Az az elhelyezés teszi az auditot önkonzisztenssé: amikor a B1-et újraszámolják, és a C1 a B1-től függ, a C1 az ezen audit menetből származó értéket látja, nem az elavult gyorsítótárazottat. Enélkül egyetlen upstream hiba egyszer megjelent volna, aztán elnyeltetett volna, és minden downstream cella úgy tűnt volna, hogy egy rossz bemenettel egyetért

Azok a cellák, amelyeknek az újraszámolt értéke egyezik a gyorsítótárral, egyáltalán nem jutnak be az overlaybe. Ez nem mikrooptimalizálás, hanem az, ami megfizethetővé teszi az auditot. Egy százezer képletű tiszta munkafüzet nulla overlay írást végez, és a menet egy teljes újraszámoláshoz képest 1,35-szörös kereten belül marad, ami az a különbség, amely eldönti, hogy valami minden beérkezéskor futtatható-e, vagy negyedévente egyszer

HotXLS deep recalc audit folyamatsor: a munkafüzet érintetlen gyorsítótárakkal töltődik be, minden függőségi csomópontot dirty-nek jelölnek, és topológiai sorrendben egyszer értékelik ki, az újraszámolt értékek elkülönített overlaybe kerülnek, amelyet mindkét motorban először kérdez le a cellaolvasási callback, az eredményeket a gyorsítótárazott értékekkel hasonlítják össze, a CalculateAndVerify TXLSCalculationAuditReportba osztályozza őket, és semmi sem íródik lemezre
Az újraszámolt értékek a cellaolvasási callback elé kerülő overlaybe kerülnek, az egyező cellák sosem nyúlnak hozzá, és a lemezen lévő munkafüzet érintetlen marad, hacsak az ApplyResults egy teljesen tiszta menetet le nem zár

A kiértékelés a függőségi gráfból levezetett soros topológiai sorrendet követ, minden csomópontot először dirty-nek jelölve, tehát minden cella pontosan egyszer számolódik a bemenetei után. Ha azt az inkrementális gépezetet szeretné, amely egy élő munkafüzetet naprakészen tart egy tárolt auditálása helyett, az más mechanizmus, amelyet az inkrementális újraszámolás és a függőségi gráf ír le

A hibák osztályozottak, nem egy kupacba rendezettek

Egy cella, amelyet az audit nem tud kiértékelni, nem ugyanaz a megállapítás, mint egy cella, amelynek az értéke eltér, és a TXLSCalculationAuditIssueKind szétválasztja a kategóriákat. A xlcaiCacheMismatch az értékeltérés. A xlcaiMissingFunction és a xlcaiMissingName azt mondja, hogy a kiértékelő olyasmibe futott bele, amelyet nem implementál vagy nem tud feloldani. A xlcaiUnsupportedArguments a támogatott részhalmazon kívüli argumentumalakokat fedi le. A xlcaiExternalReferenceDenied és a xlcaiExternalReferenceMissing egy szabályzati elutasítást választ el egy hiányzó munkafüzettől. A xlcaiCircularReference, xlcaiDataTableSkipped, xlcaiParseFailure, xlcaiCancelled és xlcaiInternalFailure teszi teljessé a halmazt

HotXLS audit megállapítások osztályozása: a TXLSCalculationAuditIssueKind elválasztja az xlcaiCacheMismatch-ként jelentett értékeltérést az olyan kiértékelési hibafajoktól, mint az xlcaiMissingFunction, xlcaiMissingName, xlcaiUnsupportedArguments, az xlcaiExternalReferenceDenied és xlcaiExternalReferenceMissing pár, valamint az xlcaiCircularReference, miközben egy pozitív Excel hibakód eredménynek számít, hibának nem
Egy fajta értékeltérést jelent, a többi azt, miért nem tudott a kiértékelő ítéletet mondani egy celláról; egy Excel hibaérték kiszámolt eredmény, tehát a szándékos hibacellák nulla megállapítást adnak

Egy megkülönböztetést érdemes kimondani, mert felfordít egy elterjedt feltevést. Egy pozitív Excel hibakód eredmény, nem hiba. Egy cella, amely jogosan #DIV/0!-ra értékelődik, helyesen számolt, tehát az audit azt a hibát az overlaybe teszi, és úgy hasonlítja a gyorsítótárhoz, mint bármely más értéket. Egy szándékos hibacellákkal teli munkafüzet nulla megállapítást ad, és egy olyan munkafüzetben, amelyben egy hiba megjelent vagy eltűnt, mióta az értékek gyorsítótárazva voltak, pontosan azok a megállapítások jelennek meg, amelyeket akar

A körkörös hivatkozások saját kezelést kapnak. A ciklusban lévő csomópontok sosem jutnak be a topológiai sorrendbe, tehát mindegyiket egyenként xlcaiCircularReference-ként jelentik, és az audit nem futtatja az iteratív megoldót. Ez szándékos csak-olvasó szerződés: az, hogy az iteráció engedélyezett-e, azt befolyásolja, hogyan kell értelmezni az eredménykódot, nem azt, mit tesz az audit. Az iteratív kiértékelés mechanikáját külön tárgyalja az iteratív számítás és körkörös hivatkozások

Egy hibalánc olvasása

Amikor egy képlet kiértékelése elbukik, ritkán elég tudni, melyik cella bukott meg, mert a hiba általában három szinttel lejjebb van egy hivatkozási láncban. Ezért minden megállapítás hordoz egy Stack sztringet, amely először a legkülső keretet rendereli, Sheet1!A1 > Sheet1!B2 > Data!C7 formában, tehát a jelentés arra a cellára mutat, amely ténylegesen eltört, nem arra, amelyre éppen ránézett

A rögzítő korlátolt. A MaxStackFrames alapértelmezése 64, alsó határa 8, és a legmélyebben bukó lánc az, amely megmarad: egy belső keret akkor rögzíti a láncot, amikor a hiba ott ered, és az utána lezáródó külső keretek nem írják felül. Ha bármely lánc túllépte a keretet, a Report.StackTruncated beállítódik, ami megmondja a különbséget egy rövid lánc és egy olyan lánc között, amelyet nem látott egészében

HotXLS audit hibalánc: amikor egy három hivatkozással lejjebb lévő képlet elbukik, a Stack először a legkülső keretet rendereli, Sheet1!A1, majd Sheet1!B2, aztán Data!C7, a legbelső keret rögzíti a láncot, és a lezáródó külső keretek nem írják felül, a MaxStackFrames alapértelmezése 64, alsó határa 8, a Report.StackTruncated pedig jelzi azt a láncot, amelyet nem látott egészében
A Stack először a legkülső keretet rendereli, hogy a jelentés arra a cellára mutasson, amely ténylegesen eltört, a legmélyebben bukó lánc az, amely megmarad, a StackTruncated pedig elkülöníti a rövid láncokat a csonkoltaktól
// Alapból csak olvas. Az ApplyResults csak egy teljesen sikeres audit után
// zárja le az overlayt, olyan írásvédő őr alatt, amely elutasítja a lezárást,
// ha a munkafüzet szerkezete megváltozott az audit futása alatt
Options := TXLSRecalcAuditOptions.Default;
Options.ApplyResults := True;
Options.AbsoluteTolerance := 0;     // pontos összehasonlítás, a sodródás látszik
Options.RelativeTolerance := 0;
Options.OnProgress := HandleProgress;

Report := Book.CalculateAndVerify(Options);
try
  if Report.Applied then
    Book.SaveToFile('quarterly-close-repaired.xls')
  else
    Writeln('not applied: ', Report.Count, ' issues blocked the commit');
finally
  Report.Free;
end;

procedure THarness.HandleProgress(ASender: TObject;
  ACurrent, ATotal: Integer; var ACancel: Boolean);
begin
  ACancel := FUserRequestedStop;   // az audit a következő csomópont-határnál áll meg
end;

Mikor engedje, hogy az audit megjavítsa a munkafüzetet?

Csak akkor, ha az audit teljesen tisztaan tért vissza hibaosztályú megállapításoktól, ami pontosan az a feltétel, amelyet az ApplyResults Ön nevében érvényesít. A lezárás egy teljesen sikeres menet után történik, nem volt megszakítva, és átmenegy egy szerkezeti őrön: a bináris motor egy munkafüzet-változásazonosítót figyel, az OOXML motor munkalankénti szerkezetgenerációt pillanatfelvételként rögzít. Ha bármi elmozdult, amíg az audit futott, az eredmények egy olyan munkafüzetet írnak le, amely már nem létezik, és a lezárást elutasítják

Vegye észre a szándékos aszimmetriát. A gyorsítótáreltérések nem blokkolják az alkalmazást, mert pontosan azok, amelyeket a lezárás javítani hivatott. A hibaosztályú megállapítások blokkolják, mert egy olyan munkafüzetben, amelyben néhány képletet nem tudtak kiértékelni, féljavított lenne az eredmény, és egy féljavított munkafüzet rosszabb, mint egy megjavítatlan, amelyről tudja, hogy ne bízzon benne

A tolerancia szabályzati döntés, nem alapértelmezés

Az alapértelmezett összehasonlítás 1E-6 abszolút tolerancia kikapcsolt relatív toleranciával, ami megtartja a klasszikus viselkedést, és csendben elfogad 4E-7 nagyságú sodródást. Ez általában helyes: a lebegőpontos kiértékelési sorrendbeli különbségek aközött, ami a fájlt előállította, és az aktuális kiértékelő között, ekkora különbségeket adnak hosszú összegeknél, és ezek integritási megállapításként való jelentése zaj

Állítsa mindkét toleranciát nullára, amikor a kérdés más: amikor azt próbálja kideríteni, hogy egy kiértékelő megváltozott-e a verziók között, vagy hogy egy harmadik féltől származó eszköz finoman eltérő módon írja-e újra az értékeket. Nullánál ugyanez a 4E-7 sodródás láthatóvá válik, és minden más is. Válassza a toleranciát aszerint, hogy melyik kérdést teszi fel, és jegyezze fel a döntést a jelentés mellé, mert egy tolerancia nélküli jelentés nem értelmezhető

Két szomszédos képesség teszi teljessé a képet. Amikor tudni akarja, miért azt az értéket adja egyetlen képlet, amelyet ad, a képlet-kiértékelő tracer lépésenkénti nézete a helyes eszköz. Amikor szándékosan azt akarja, hogy a gyorsítótárazott értékek érvényesüljenek bármilyen újraszámolás nélkül, például egy olyan beérkező úton, amelynek a fájlt pontosan úgy kell visszaadnia, ahogy megérkezett, azt a módot az gyorsítótárazott képletértékek olvasása újraszámolás nélkül írja le. Az audit az, amely a kettő között ül: megmondja, hogy a gyorsítótárba vetett bizalom biztonságos-e. A HotXLS Delphi táblázatkezelő komponenssel szállul a bináris és az OOXML motorhoz egyaránt