API kontingenčních tabulek klasického XLS
HotXLS podporuje dva klasické pracovní postupy kontingenčních tabulek XLS. Sešity otevřené z Excelu si ponechají původní zobrazení kontingenční tabulky i záznamy mezipaměti kontingenční tabulky pro zpětné uložení. Kód může také zkoumat typovaný model kontingenční tabulky a vytvářet nové klasické kontingenční tabulky XLS přes
Přenos a typizovaný model
Když sešit už obsahuje kontingenční tabulky, čtečka zachová nezpracované záznamy BIFF a zároveň zpřístupní nejlepší odhad typovaného modelu. Tabulky a mezipaměti načtené ze souboru si ponechají FromRawBlobs = True, takže uložený výstup přehraje původní bajty pro co nejvyšší věrnost. Programově vytvořené kontingenční tabulky používají typovaný zapisovač
const
xlPivotBaseItemPrevious = $7FFB;
xlPivotBaseItemNext = $7FFC;
type
TXLSPivotFieldAxis = (xlpfaNone, xlpfaRowField, xlpfaColumnField, xlpfaPageField, xlpfaDataField);
TXLSPivotAggregation = (xlpaSum, xlpaCount, xlpaAverage, xlpaMax, xlpaMin, xlpaProduct, xlpaCountNums, xlpaStdDev, xlpaStdDevP, xlpaVar, xlpaVarP);
TXLSPivotShowDataAs = (xlpsdaNormal, xlpsdaDifference, xlpsdaPercent, xlpsdaPercentDiff, xlpsdaRunTotal, xlpsdaPercentOfRow, xlpsdaPercentOfCol, xlpsdaPercentOfTotal, xlpsdaIndex);
TXLSPivotExtendedShowDataAs = (xlpesdaNone, xlpesdaPercentOfParent, xlpesdaPercentOfParentRow, xlpesdaPercentOfParentColumn, xlpesdaPercentOfRunningTotal, xlpesdaRankAscending, xlpesdaRankDescending);
TXLSPivotSubtotal = (xlpsDefault, xlpsSum, xlpsCount, xlpsAverage, xlpsMax, xlpsMin, xlpsProduct, xlpsCountNums, xlpsStdDev, xlpsStdDevP, xlpsVar, xlpsVarP);
TXLSPivotSubtotals = set of TXLSPivotSubtotal;
TXLSPivotDesignerZone = (xlpdzAvailable, xlpdzRows, xlpdzColumns, xlpdzFilters, xlpdzValues);
TXLSPivotCacheValue = record
ValueType: TXLSPivotCacheFieldType;
end;
TXLSPivotCacheField = class
property Name: WideString;
property FieldType: TXLSPivotCacheFieldType;
property ItemCount: Integer;
property Items[Index: Integer]: TXLSPivotCacheItem;
end;
TXLSPivotCache = class
property CacheId: Integer;
property SourceRangeSheet: WideString;
property SourceFirstRow: Integer;
property SourceFirstCol: Integer;
property SourceLastRow: Integer;
property SourceLastCol: Integer;
property RecordCount: Integer;
property FieldCount: Integer;
property Fields[Index: Integer]: TXLSPivotCacheField;
property RecordIndices[RecordIdx, FieldIdx: Integer]: Integer;
property FromRawBlobs: Boolean;
end;
TXLSPivotCaches = class
function Add: TXLSPivotCache;
function FindByCacheId(ACacheId: Integer): TXLSPivotCache;
property Count: Integer;
property Items[Index: Integer]: TXLSPivotCache;
end;
TXLSPivotField = class
function AddItem: TXLSPivotItem;
function AddCalculatedItem(const AName, AFormula: WideString): TXLSPivotCalculatedItem;
property Name: WideString;
property Axis: TXLSPivotFieldAxis;
property CacheFieldIndex: Integer;
property ItemCount: Integer;
property Items[Index: Integer]: TXLSPivotItem;
property SubtotalMask: Word;
property Subtotals: TXLSPivotSubtotals;
property PageItemIndex: Integer;
property SortType: TXLSPivotFieldSortType;
property SortDataField: Integer;
property Filters: TXLSPivotFilters;
end;
TXLSPivotDataField = class
property Field: TXLSPivotField;
property Aggregation: TXLSPivotAggregation;
property NumberFormat: Word;
property DisplayName: WideString;
property ShowDataAs: TXLSPivotShowDataAs;
property ExtendedShowDataAs: TXLSPivotExtendedShowDataAs;
property BaseField: Integer;
property BaseItem: Integer;
end;
TXLSPivotSupplementalRecord = class
property RecID: Word;
property BodyLength: Integer;
property Body[Index: Integer]: Byte;
end;
TXLSPivotTable = class
function AddField(const AName: WideString; ACacheFieldIndex: Integer): TXLSPivotField;
function FindFieldByName(const AName: WideString): TXLSPivotField;
function SetFieldAxis(const AName: WideString; AAxis: TXLSPivotFieldAxis): TXLSPivotField;
function AddRowField(const AName: WideString): TXLSPivotField;
function AddColumnField(const AName: WideString): TXLSPivotField;
function AddPageField(const AName: WideString): TXLSPivotField;
function AddDataField(AField: TXLSPivotField; AAggregation: TXLSPivotAggregation): TXLSPivotDataField;
function AddDataFieldByName(const AName: WideString; AAggregation: TXLSPivotAggregation = xlpaSum): TXLSPivotDataField;
function AddCalculatedMember(const AName, AFormula: WideString): TXLSPivotCalculatedMember;
function AddSupplementalRecord(ARecID: Word; const ABody: TBytes): TXLSPivotSupplementalRecord;
function Make(AWriter: TlxPivotResultWriter): Integer;
property Name: WideString;
property DataCaption: WideString;
property CacheId: Integer;
property Cache: TXLSPivotCache;
property FieldCount: Integer;
property Fields[Index: Integer]: TXLSPivotField;
property DataFieldCount: Integer;
property DataFields[Index: Integer]: TXLSPivotDataField;
property RowGrandTotals: Boolean;
property ColumnGrandTotals: Boolean;
property MaterializeGrandTotalRow: Boolean;
property MaterializeGrandTotalColumn: Boolean;
property SupplementalRecordCount: Integer;
property SupplementalRecords[Index: Integer]: TXLSPivotSupplementalRecord;
property FromRawBlobs: Boolean;
end;
TXLSPivotDesigner = class
constructor Create(APivotTable: TXLSPivotTable = nil);
procedure ClearLayout;
function MoveField(const AName: WideString; AZone: TXLSPivotDesignerZone; APosition: Integer = -1): Boolean;
function SetAggregation(const AName: WideString; AAggregation: TXLSPivotAggregation): Boolean;
function Validate(out ErrorText: WideString): Boolean;
function Apply(AWriter: TlxPivotResultWriter): Integer;
property PivotTable: TXLSPivotTable;
property FieldCount: Integer;
property Fields[Index: Integer]: TXLSPivotDesignerField;
end;
TXLSPivotTables = class
function Add: TXLSPivotTable;
function FindByName(const AName: WideString): TXLSPivotTable;
property Count: Integer;
property Items[Index: Integer]: TXLSPivotTable;
end;
Vstupní body listu a sešitu
type
TXLSWorksheet = class
function AddPivotTable(const SourceRangeA1: WideString; DestRow, DestCol: Integer; const Name: WideString): TXLSPivotTable;
function PivotSetDataFieldFormat(DataField: TXLSPivotDataField; const FormatStr: WideString): Word;
property PivotTables: TXLSPivotTables;
end;
TXLSWorkbook = class
property PivotCaches: TXLSPivotCaches;
function RegisterNumFormat(const FormatStr: WideString): Word;
end;
RegisterNumFormat zaregistruje řetězec číselného formátu a vrátí jeho index ifmt podle BIFF8, který očekávají TXLSPivotDataField.NumberFormat a TXLSPivotCacheField.NumberFormat ; opakovaná registrace téhož řetězce vrátí tentýž index
DestRow a DestCol se počítají od 1, stejně jako Cells[Row, Col], takže AddPivotTable('Data!A1:C9', 3, 1, 'SalesPivot') ukotví kontingenční tabulku do A3. Pozice na TXLSPivotTable (FirstRow .. FirstDataCol) i zdrojová oblast na TXLSPivotCache se počítají také od 1, stejně jako v XLSX enginu; ukotvení za hranicemi listu 65,536 x 256 vrátí nil
Sestavení rozvržení kontingenční tabulky drag-and-drop
TXLSPivotDesigner je nevizuální model rozvržení, který může podkládat field-list rozhraní ve VCL, FMX, na webu nebo v příkazové řádce, aniž by vázal logiku kontingenční tabulky na jednu sadu UI komponent
Každé pole hlásí dostupnou zónu, zónu řádků, sloupců, filtrů nebo hodnot plus svou deterministickou pozici; MoveField aplikuje přetažení, SetAggregation změní hodnotové pole, Validate hlásí nekompletní rozvržení a Apply výsledek zvaliduje a materializuje běžným zapisovačem kontingenčních tabulek
Nová číselná a datumová hodnotová pole mají výchozí xlpaSum, zatímco textová, logická, chybová, prázdná a smíšená pole mají výchozí xlpaCount
var
Designer: TXLSPivotDesigner;
ErrorText: WideString;
begin
Designer := TXLSPivotDesigner.Create(Pivot);
try
Designer.MoveField('Region', xlpdzRows);
Designer.MoveField('Quarter', xlpdzColumns);
Designer.MoveField('Closed', xlpdzFilters);
Designer.MoveField('Revenue', xlpdzValues);
Designer.SetAggregation('Revenue', xlpaAverage);
if Designer.Validate(ErrorText) then
Designer.Apply(ResultWriter.WriteCell);
finally
Designer.Free;
end;
end;
Vytvoření kontingenční tabulky
uses lxHandle;
var
Workbook: TXLSWorkbook;
SourceSheet, PivotSheet: TXLSWorksheet;
Pivot: TXLSPivotTable;
RowField: TXLSPivotField;
DataField: TXLSPivotDataField;
begin
Workbook := TXLSWorkbook.Create;
try
SourceSheet := Workbook.Sheets[1];
SourceSheet.Name := 'SalesData';
SourceSheet.Cells[1, 1].Value := 'Region';
SourceSheet.Cells[1, 2].Value := 'Product';
SourceSheet.Cells[1, 3].Value := 'Revenue';
PivotSheet := Workbook.Sheets.Add;
PivotSheet.Name := 'PivotReport';
Pivot := PivotSheet.AddPivotTable('SalesData!A1:C20', 4, 1, 'SalesPivot');
RowField := Pivot.AddRowField('Region');
Pivot.AddColumnField('Product');
DataField := Pivot.AddDataFieldByName('Revenue', xlpaSum);
RowField.Subtotals := [xlpsSum];
PivotSheet.PivotSetDataFieldFormat(DataField, '$#,##0.00');
Workbook.SaveAs('pivot-report.xls');
finally
Workbook.Free;
end;
end;
Zobrazení hodnot jako
Nastavte ShowDataAs u každého datového pole před voláním Make nebo před uložením sešitu. Režimy Difference, Percenta PercentDiff vyžadují index pole kontingenční tabulky BaseField od nuly plus BaseItem. RunTotal potřebuje jen BaseField. Řádkový, sloupcový, součtový a indexový režim si jmenovatele odvozují ze zdrojových záznamů s nastavenou agregací
Explicitní BaseItem je index položky od nuly uvnitř základního pole kontingenční tabulky. Pro porovnání sousedních položek použijte xlPivotBaseItemPrevious nebo xlPivotBaseItemNext . Chybějící průsečíky porovnání zůstanou prázdné, zatímco existující nulový jmenovatel vyprodukuje #DIV/0!
DataField := Pivot.AddDataFieldByName('Revenue', xlpaSum);
DataField.ShowDataAs := xlpsdaPercentDiff;
DataField.BaseField := 0;
DataField.BaseItem := xlPivotBaseItemPrevious;
CellsWritten := Pivot.Make(ResultWriter.WriteCell);
Filtrování, seskupování a řazení
Make nejprve před stavbou domény aplikuje skryté položky a výběry stránek a poté vyhodnotí filtry podle titulku, data, počtu, procent, součtu a hodnoty. Číselné rozsahy, kalendářní skupiny a diskrétní skupiny používají před agregací metadata seskupení pole mezipaměti. SortType volí stabilní pořadí podle popisků a SortDataField určuje hodnotové pole od nuly pro stabilní pořadí podle datových hodnot
Počítané výrazy
Počítaná pole mezipaměti se vyhodnocují jednou pro každý zdrojový záznam, počítané položky přes agregáty sousedních položek a počítané členy s MemberType = 'data' přidávají virtuální hodnotová pole po agregaci zdroje. Názvy vzorců nerozlišují velikost písmen a mohou být uzavřeny v hranatých závorkách nebo v uvozovkách. Podporovaná je aritmetika, porovnání, procenta, závorky a běžné číselné i logické funkce, přičemž pro acyklické počítané definice se průchod závislostmi opakuje
ProfitField := Pivot.Cache.AddField('Profit', xlpcftNumber);
ProfitField.Formula := '=Revenue-Cost';
ProfitField.DatabaseField := False;
Pivot.AddDataField(Pivot.AddField('Profit', 4), xlpaSum);
RegionField.AddCalculatedItem('Combined', '=North+South');
Pivot.AddCalculatedMember('Margin', '=Profit/Revenue').MemberType := 'data';
Mezisoučty a celkové součty
Každé řádkové nebo sloupcové pole může přes Subtotals a SubtotalTopmaterializovat libovolnou nastavenou funkci mezisoučtu nad nebo pod svými podrobnými položkami. RowGrandTotals a ColumnGrandTotals řídí politiku ukládaného sešitu, zatímco MaterializeGrandTotalRow a MaterializeGrandTotalColumn zapnou explicitní výstup celkových součtů ve výsledných zpětných voláních bez opakovaného proskenu zdrojových záznamů
Rozšířené režimy zobrazení hodnot
ExtendedShowDataAs nabízí procento z nadřazeného prvku, procento z nadřazeného řádku, procento z nadřazeného sloupce, procento z běžícího součtu a pořadí (rank) vzestupně či sestupně. Tyto režimy používají rozšiřující model XLSX a negenerují nepodporované hodnoty datových polí klasického BIFF
Kontrola existujícího sešitu s kontingenčními tabulkami
Workbook.Open('excel-authored-pivot.xls');
for SheetIndex := 1 to Workbook.Sheets.Count do
begin
Sheet := Workbook.Sheets[SheetIndex];
for PivotIndex := 0 to Sheet.PivotTables.Count - 1 do
begin
Pivot := Sheet.PivotTables[PivotIndex];
Writeln(Pivot.Name);
Writeln(Pivot.FieldCount);
if Assigned(Pivot.Cache) then
Writeln(Pivot.Cache.SourceRangeSheet);
end;
end;