Teknisk artikkel

Strukturerte Excel-tabellreferanser i Delphi med HotXLS

HotXLS evaluerer nå strukturerte tabellreferanser, slik at =SUM(Table1[Amount]) produserer et tall i stedet for å hoppes over. Resolveren håndterer Table[Column], Table[[Column]], kolonnespenn som Table[[Q1]:[Q4]], og elementspesifikatorene [#Data], [#All], [#Headers] og [#Totals], og løser opp hver av dem mot arbeidsbokens tabellmodell ved analysetidspunktet, mens den opprinnelige formelteksten rundtripper ordrett

Én form er bevisst fraværende, og det er den folk treffer på først. Kortformen for gjeldende rad, [@Column], støttes ikke, av en strukturell grunn som er verdt å forstå fremfor å omgå blindt

Hvorfor er ikke en strukturert referanse bare et område med et vennlig navn?

Fordi et definert navn fryser en adresse, og en tabellreferanse gjør ikke det. Skriv DataBlock som et navn som peker på Sheet1!$A$2:$D$100, og det forblir det rektangelet inntil noe skriver det om. Skriv Sales[Amount], og det betyr «Amount-kolonnen i Sales-tabellen», uansett hva den tabellens utstrekning tilfeldigvis er når formelen evalueres. Legg til tjue rader i tabellen, og summen dekker dem; det finnes ingen referanse å justere fordi det aldri fantes en adresse i formelen i utgangspunktet

Denne symbolske egenskapen er nøyaktig grunnen til at referansen ikke kan løses ved strengsubstitusjon. Resolveren må finne tabellen ved navn i arbeidsboken, slå opp kolonnen etter overskriftsteksten, avgjøre hvilke rader den forespurte elementspesifikatoren dekker, og produsere et konkret rektangel. HotXLS gjør dette under formelkompilering gjennom tabellmodellen, som er grunnen til at en formel skrevet før tabellen vokser, fortsatt evaluerer mot tabellens nåværende utstrekning

Grammatikken HotXLS løser opp

Den støttede spesifikasjonsgrammatikken dekker ett enkelt rektangulært resultat og er verdt å angi presist, fordi Excels dokumentasjon presenterer en langt større overflate enn de fleste motorer implementerer. HotXLS godtar [Col] og den klammeromsluttede varianten [[Col]], de bare elementspesifikatorene [#Data], [#All], [#Headers] og [#Totals], den kombinerte formen [[#Data],[Col]], et spenn inne i en elementspesifikator som [[#Data],[Col1]:[Col2]], og et vanlig spenn [Col1]:[Col2]

Det settet gir deg hver referanseform som produserer én sammenhengende blokk: en kolonne, en rekke tilstøtende kolonner, en kropp-bare eller overskrift-inkludert skive av begge. Ikke-tilstøtende unioner og resultater med flere områder ligger utenfor det. Når en referanse ikke kan løses, beholder formelen den tidligere hopp-over-uten-verdi-atferden fremfor å erstatte med en gjetning, slik at en uløselig referanse aldri blir et plausibelt feil tall

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Cols: TStringList;
begin
  Book := TXLSXWorkbook.Create;
  Cols := TStringList.Create;
  try
    Sheet := Book.Sheets.Add('Sales');
    Cols.Add('Region');
    Cols.Add('Q1');
    Cols.Add('Q2');
    Cols.Add('Amount');
    Sheet.Tables.Add('SalesTable', 'A1:D25', Cols);
    // ... skriv overskriftsraden og 24 datarader ...

    Sheet.Cells[27, 4].Formula := 'SUM(SalesTable[Amount])';
    Sheet.Cells[28, 4].Formula := 'SUM(SalesTable[[Q1]:[Q2]])';
    Sheet.Cells[29, 4].Formula := 'COUNTA(SalesTable[[#Data],[Region]])';
    Sheet.Cells[30, 4].Formula := 'ROWS(SalesTable[#All])';

    Book.Recalculate;
    Book.SaveAs('sales.xlsx');
  finally
    Cols.Free;
    Book.Free;
  end;
end;

Hvorfor er gjeldende-rad-formen bevisst ekskludert?

[@Column] og [#This Row] betyr «cellen i den kolonnen på raden der denne formelen bor». Verdien avhenger derfor av den evaluerende cellens posisjon, ikke bare av tabellen. Det er en annen type referanse: ikke et rektangel kompilatoren kan løse én gang, men en per-celle-oppløsning som må gjøres på nytt for hver rad formelen opptar

HotXLS returnerer False fra tabellområde-resolveren for disse formene, noe som sender dem inn på hopp-over-uten-verdi-veien. Formelteksten bevares og skrives tilbake uendret, slik at en arbeidsbok som bruker [@Amount], åpnes korrekt i Excel etter en rundtripp gjennom applikasjonen din; bare den HotXLS-beregnede verdien er fraværende. Gitt valget mellom en fraværende verdi og en verdi beregnet mot feil rad, er fravær det du kan oppdage

Den praktiske løsningen er mekanisk: i en arbeidsbok du genererer, skriv den tilsvarende relative A1-stil-referansen, som er det Excel uansett lagrer internt for mye av den tabellbundne logikken. I en arbeidsbok du bare prosesserer, la formelen være i fred og les den bufrede verdien Excel allerede lagret, som er det en last-og-rapporter-pipeline vanligvis vil ha

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Table: TXLSXTable;
  Row: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open('sales.xlsx') <> 1 then Exit;
    Sheet := Book.Sheets[1];

    Table := Sheet.Tables.FindByName('SalesTable');
    if Table <> nil then
    begin
      // Recordset-aktig oppslag over tabellkroppen, 1-basert radresultat
      Row := Table.FindFirst(Sheet, 'Region', 'EMEA');
      while Row > 0 do
      begin
        Log(VarToStr(Sheet.Cells[Row, 4].Value));
        Row := Table.FindNext(Sheet, 'Region', 'EMEA', Row);
      end;
    end;
  finally
    Book.Free;
  end;
end;

Hva skjer når tabellen endrer form

Strukturerte referanser ugyldiggjøres i stedet for stille å bli omdirigert når det de navngir, forsvinner. Slett en kolonne, og formler som refererer til den kolonnen, ugyldiggjøres slik Excel ugyldiggjør dem; slett eller gi tabellen nytt navn, og referanser til den håndteres på samme måte. Dette er den korrekte atferden, og den speiler vanlig referansejustering, beskrevet i formelreferansejustering ved innsetting og sletting, der motorens jobb er å holde formler ærlige fremfor å få dem til å se gyldige ut

Radvekst er det motsatte tilfellet og trenger ingen justering i det hele tatt. Fordi referansen navngir tabellen fremfor et rektangel, utvider tillegg av rader inne i tabellens område det [#Data] dekker, uten å røre en eneste formel. Det er egenskapen som gjør tabeller verdt å bruke i en rapportmal: totalsummeraden fortsetter å summere alt importen produserte, uansett hvor mange rader det viste seg å bli

Rundtrippdisiplin

HotXLS beholder den opprinnelige formelteksten. En arbeidsbok lastet inn med SUM(SalesTable[Amount]) lagres med SUM(SalesTable[Amount]), ikke med den oppløste SUM(D2:D25). Dette betyr mer enn det kan virke som: en bruker som åpner utdataen din i Excel, forventer å se formelen de skrev, og en oppløst adresse ville stille konvertert en selvvedlikeholdende modell til en skjør en som slutter å dekke nye rader

To beslektede egenskaper fullfører bildet. Selve tabelldefinisjonene, inkludert overskriftsløse tabeller og per-tabell-kommentarer, rundtripper gjennom tabellmodellen beskrevet i datavalidering, AutoFilter og Excel-tabeller. Og når mange celler deler ett mønster, lagrer XLSX dem én gang som en delt formel, som utvides og skrives ut på nytt som dekket i utvidelse av delte formler (si). Strukturerte referanser inne i delte formler går gjennom begge stiene, så begge må oppføre seg riktig, og det gjør de

HotXLS leser og skriver XLS, XLSX og ODS fra Delphi og C++Builder uten at Excel er installert og uten Office-automatisering, og evaluerer formler i sin egen motor. Tabellmodellen, formelmotoren og omregnings-API-et er dokumentert på HotXLS sin side for Delphi regnearkkomponent