O HotXLS agora avalia referências estruturadas de tabela, então =SUM(Table1[Amount]) produz um número em vez de ser ignorado. O resolvedor lida com Table[Column], Table[[Column]], intervalos de colunas como Table[[Q1]:[Q4]], e os especificadores de item [#Data], [#All], [#Headers] e [#Totals], resolvendo cada um contra o modelo de tabelas da pasta de trabalho no momento do parsing, enquanto o texto original da fórmula faz o round-trip literalmente
Uma forma está deliberadamente ausente, e é a primeira que as pessoas encontram. O atalho de linha atual [@Column] não é suportado, por um motivo estrutural que vale a pena entender, em vez de contornar às cegas
Por que uma referência estruturada não é apenas um intervalo com um nome amigável?
Porque um nome definido congela um endereço, e uma referência de tabela não. Escreva DataBlock como um nome apontando para Sheet1!$A$2:$D$100 e ele permanece aquele retângulo até que algo o reescreva. Escreva Sales[Amount] e isso significa "a coluna Amount da tabela Sales", seja qual for a extensão daquela tabela no momento em que a fórmula é avaliada. Adicione vinte linhas à tabela e a soma vai cobri-las; não há referência a ajustar porque nunca existiu, para começar, um endereço na fórmula
Essa qualidade simbólica é exatamente o motivo pelo qual a referência não pode ser resolvida por substituição de string. O resolvedor precisa encontrar a tabela pelo nome na pasta de trabalho, procurar a coluna pelo texto do seu cabeçalho, decidir quais linhas o especificador de item solicitado cobre, e produzir um retângulo concreto. O HotXLS faz isso durante a compilação da fórmula por meio do modelo de tabelas, e é por isso que uma fórmula escrita antes de a tabela crescer ainda é avaliada contra a extensão atual da tabela
A gramática que o HotXLS resolve
A gramática suportada cobre um único resultado retangular e vale a pena declará-la com precisão, porque a documentação do Excel apresenta uma superfície muito maior do que a maioria dos motores implementa. O HotXLS aceita [Col] e a variante entre colchetes duplos [[Col]], os especificadores de item simples [#Data], [#All], [#Headers] e [#Totals], a forma combinada [[#Data],[Col]], um intervalo dentro de um especificador de item como [[#Data],[Col1]:[Col2]], e um intervalo simples [Col1]:[Col2]
O que esse conjunto oferece é toda forma de referência que produz um único bloco contíguo: uma coluna, uma sequência de colunas adjacentes, uma fatia somente do corpo ou incluindo o cabeçalho de qualquer uma delas. Uniões não adjacentes e resultados de múltiplas áreas ficam fora dele. Quando uma referência não pode ser resolvida, a fórmula mantém o comportamento anterior de pular sem valor em vez de substituir por um palpite, então uma referência não resolvível nunca vira um número errado plausível
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);
// ... grave a linha de cabeçalho e as 24 linhas de dados ...
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;
Por que a forma de linha atual é excluída de propósito?
[@Column] e [#This Row] significam "a célula daquela coluna na linha onde esta fórmula está". O valor, portanto, depende da posição da célula que está avaliando, não só da tabela. Esse é um tipo diferente de referência: não um retângulo que o compilador pode resolver uma vez, mas uma resolução por célula que precisa ser refeita para cada linha que a fórmula ocupa
O HotXLS retorna False do resolvedor de intervalo de tabela para essas formas, o que as encaminha para o caminho de pular sem valor. O texto da fórmula é preservado e gravado de volta sem alterações, então uma pasta de trabalho que usa [@Amount] abre corretamente no Excel depois de um round-trip pela sua aplicação; apenas o valor calculado pelo HotXLS está ausente. Diante da escolha entre um valor ausente e um valor calculado contra a linha errada, a ausência é a que você consegue detectar
A solução alternativa prática é mecânica: em uma pasta de trabalho que você gera, escreva a referência relativa equivalente no estilo A1, que é o que o Excel armazena internamente de qualquer forma para boa parte da lógica com escopo de tabela. Em uma pasta de trabalho que você apenas processa, deixe a fórmula em paz e leia o valor em cache que o Excel já armazenou, que é o que um pipeline de carregar-e-relatar normalmente quer
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
// Busca no estilo recordset sobre o corpo da tabela, resultado de linha baseado em 1
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;
O que acontece quando a tabela muda de forma
Referências estruturadas são invalidadas em vez de silenciosamente redirecionadas quando aquilo que elas nomeiam deixa de existir. Exclua uma coluna e as fórmulas que se referem a ela são invalidadas da mesma forma que o Excel as invalida; exclua ou renomeie a tabela e as referências a ela são tratadas da mesma maneira. Esse é o comportamento correto e espelha o ajuste de referência comum, descrito em ajuste de referência de fórmula em inserção e exclusão, onde o trabalho do motor é manter as fórmulas honestas, e não mantê-las com aparência válida
O crescimento de linhas é o caso oposto e não precisa de nenhum ajuste. Como a referência nomeia a tabela, e não um retângulo, anexar linhas dentro do intervalo da tabela amplia o que [#Data] cobre sem tocar em uma única fórmula. Essa é a propriedade que torna as tabelas valiosas em um template de relatório: a linha de totais continua somando tudo o que a importação produziu, não importa quantas linhas isso tenha resultado
Disciplina de round-trip
O HotXLS mantém o texto original da fórmula. Uma pasta de trabalho carregada com SUM(SalesTable[Amount]) é salva com SUM(SalesTable[Amount]), e não com o SUM(D2:D25) resolvido. Isso importa mais do que pode parecer: um usuário que abre a sua saída no Excel espera ver a fórmula que escreveu, e um endereço resolvido converteria silenciosamente um modelo que se auto-mantém em um modelo frágil que deixa de cobrir linhas novas
Dois recursos relacionados completam o quadro. As próprias definições de tabela, incluindo tabelas sem cabeçalho e comentários por tabela, fazem o round-trip por meio do modelo de tabelas descrito em validação de dados, AutoFilter e tabelas do Excel. E quando muitas células compartilham um padrão, o XLSX as armazena uma vez como uma fórmula compartilhada, que é expandida e reemitida, como abordado em expansão de si de fórmula compartilhada. Referências estruturadas dentro de fórmulas compartilhadas passam pelos dois caminhos, então os dois precisam se comportar bem, e se comportam
O HotXLS lê e grava XLS, XLSX e ODS a partir de Delphi e C++Builder sem instalação do Excel e sem automação do Office, avaliando fórmulas em seu próprio motor. O modelo de tabelas, o motor de fórmulas e a API de recálculo estão documentados na página do componente HotXLS Delphi para planilhas