Todo desenvolvedor de sistemas de contabilidade enfrenta a tarefa de analisar grandes volumes de dados. A transformação de «papa informacional» em relatórios estruturados é fundamental para a tomada de decisões de gestão. Isso se aplica, por exemplo, a:
Por exemplo, os OLAP cubes (FastCube) permitem resolver essas tarefas de forma rápida, visual e sem escrever consultas SQL complexas. Ao contrário das tabelas bidimensionais, as tabelas dinâmicas multidimensionais possibilitam examinar os dados sob diferentes ângulos (fatias) em tempo real.
Neste artigo, descreveremos passo a passo a criação de um aplicativo analítico em Delphi usando os componentes TfrGrid (para os dados de origem) e TfcxSliceGrid (para construir a tabela dinâmica).
Antes de construir a analítica, precisamos ver a amostra de dados de origem. Como exemplo, tomaremos os dados de uma empresa de engenharia mecânica referentes ao ano de 2025.
Precisaremos do componente TfrGrid, ele faz parte do FastReport VCL e está disponível gratuitamente na versão demo.
O principal recurso do componente de grid — a edição direta das células (alteração dos dados de origem no campo do banco de dados). No FastGrid isso é implementado por meio da inserção automática de editores conforme o tipo de campo. Por exemplo:
Importante: neste ponto temos uma interface para visualizar e corrigir manualmente os dados de origem. Isso ainda não é o resultado final da analítica.
Agora vamos ao instrumento principal — a tabela dinâmica. Para isso, usaremos o componente TfcxSliceGrid e o vincularemos à mesma fonte de dados do TfrGrid.
Para trabalhar confortavelmente com períodos de tempo, é necessário configurar o fracionamento da data:
A seguir, escreveremos um manipulador do evento OnShow do formulário, para não ter de arrastar os campos com o mouse a cada inicialização.
uses …, fcxTypes; procedure TForm1.FormShow(Sender: TObject); var LIndex: Integer; begin fcxSlice1.BeginUpdate; try // Adding fields to the Filters area fcxSlice1.PageContainer.AddFilterField(fcxSlice1.SliceFields.ItemByName['Machine'], 'Machine', 'Machine'); fcxSlice1.PageContainer.AddFilterField(fcxSlice1.SliceFields.ItemByName['Date'], 'Date', 'Date'); fcxSlice1.PageContainer.AddFilterField(fcxSlice1.SliceFields.ItemByName['Department'], 'Department', 'Work Area'); fcxSlice1.PageContainer.AddFilterField(fcxSlice1.SliceFields.ItemByName['Shift'], 'Shift', 'Shift'); fcxSlice1.PageContainer.AddFilterField(fcxSlice1.SliceFields.ItemByName['Operator'], 'Operator', 'Operator'); fcxSlice1.PageContainer.AddFilterField(fcxSlice1.SliceFields.ItemByName['Product'], 'Product', 'Product'); fcxSlice1.PageContainer.AddFilterField(fcxSlice1.SliceFields.ItemByName['Product_Category'], 'Product_Category', 'Product Category'); fcxSlice1.PageContainer.AddFilterField(fcxSlice1.SliceFields.ItemByName['Work_Order'], 'Work_Order', 'Product Order'); fcxSlice1.PageContainer.AddFilterField(fcxSlice1.SliceFields.ItemByName['Downtime_Reason'], 'Downtime_Reason', 'Downtime Reason'); fcxSlice1.PageContainer.AddFilterField(fcxSlice1.SliceFields.ItemByName['Plant'], 'Plant', 'Workshop'); // Adding fields to the Rows area fcxSlice1.YAxisContainer.AddDimension(fcxSlice1.SliceFields.ItemByName['Supervisor'], 'Supervisor', 'Foreman'); // Setting up data fields LIndex := fcxSlice1.MeasuresContainer.AddMeasure(fcxSlice1.SliceFields.ItemByName['Planned_Qty'], 'Planned_Qty', 'Plan, pcs', af_Sum); fcxSlice1.MeasuresContainer.Measures[LIndex].Visible := False; fcxSlice1.MeasuresContainer.AddMeasure(fcxSlice1.SliceFields.ItemByName['Produced_Qty'], 'Produced_Qty', 'Produced, pcs', af_Sum); fcxSlice1.MeasuresContainer.AddMeasure(fcxSlice1.SliceFields.ItemByName['Defective_Qty'], 'Defective_Qty', 'Defects, pcs', af_Sum); LIndex := fcxSlice1.MeasuresContainer.AddMeasure(fcxSlice1.SliceFields.ItemByName['Operating_Hours'], 'Operating_Hours', 'Operating Time, h', af_Sum); fcxSlice1.MeasuresContainer.Measures[LIndex].Visible := False; LIndex := fcxSlice1.MeasuresContainer.AddMeasure(fcxSlice1.SliceFields.ItemByName['Downtime_min'], 'Downtime_min', 'Downtime, min', af_Sum); fcxSlice1.MeasuresContainer.Measures[LIndex].Visible := False; LIndex := fcxSlice1.MeasuresContainer.AddMeasure(fcxSlice1.SliceFields.ItemByName['Unit_Cost'], 'Unit_Cost', 'Unit Cost', af_Sum); fcxSlice1.MeasuresContainer.Measures[LIndex].Visible := False; // Adding fields to the Data area fcxSlice1.XAxisContainer.AddMeasuresField; finally // The table is rebuilt only here fcxSlice1.EndUpdate; end; end;
O uso de BeginUpdate e EndUpdate é fundamental para o desempenho, pois evita múltiplos redesenhos da interface ao adicionar cada campo.
Depois de gerar o relatório, podemos encontrar anomalias instantaneamente, simplesmente arrastando os campos com o mouse (Drag-and-Drop). Vamos examinar 3 exemplos de como o OLAP ajuda em tarefas reais.
No grid obtido, definimos os seguintes agrupamentos: «Mestre» no eixo Y, «Refugo» no eixo X.
Tiramos a conclusão. Se o nível de refugo do mestre Smirnov é suspeitamente baixo, esse é um sinal para estudar seus métodos de trabalho ou um motivo para premiá-lo. Os dados indicam onde procurar as melhores práticas.
Alteramos as regras de agrupamento. Agora «Turno» — no eixo Y; «Plano, Produção, Refugo» — no eixo X.
Com base nesses dados, concluímos que o terceiro turno apresenta um plano baixo, mas um nível alto de refugo. Isso pode indicar ineficiência na organização do trabalho no período noturno.
Novamente alteramos os parâmetros de agrupamento. Desta vez «Tipo de máquina» no eixo Y, «Refugo» no eixo X.
Resumindo. Se o valor de refugo de um determinado tipo de máquina excede substancialmente o indicador médio, é necessária a troca do ferramental ou um reparo.
Com o FastCube, é possível construir muitas fatias de complexidade variada:
Funções importantes:
A combinação FastGrid + FastCube no ambiente Delphi/Lazarus — é uma solução pronta para a introdução de BI analítico em aplicativos corporativos. Você recebe:
Experimente todas as funções mencionadas na versão demo do FastReport VCL em nosso site.