0

I am starting my first app that needs an embedded database, ability to import a CSV into a table and display the results into a Grid. I used the Live Binding Wizard to link my grid to a BindSourceDB. Here are the basic components I am using:

BindingsList1: TBindingsList;
LinkFillControlToField1: TLinkFillControlToField;
BindSourceDB1: TBindSourceDB;
FireTaskList: TFDConnection;
FDTableTask: TFDTable;
FDQuery: TFDQuery;
FDGUIxWaitCursor1: TFDGUIxWaitCursor;
FDPhysIBDriverLink1: TFDPhysIBDriverLink;
FDTableTaskDATE: TDateField;
FDTableTaskDESCRIPTION: TStringField;
FDTableTaskORIGDESC: TStringField;
FDTableTaskAMOUNT: TIntegerField;
FDTableTaskTYPE: TStringField;
FDTableTaskCATEGORY: TStringField;
FDTableTaskACCTNAME: TStringField;
FDTableTaskLABELS: TStringField;
FDTableTaskNOTES: TMemoField;
FDBatchMove1: TFDBatchMove;
FDBatchMoveTextReader1: TFDBatchMoveTextReader;
FDBatchMoveDataSetWriter1: TFDBatchMoveDataSetWriter;
OpenDialog1: TOpenDialog;
Grid1: TGrid;
LinkGridToDataSourceBindSourceDB1: TLinkGridToDataSource;

1st problem, it is a real slow process importing a file with 7 total columns and total of 5500 lines or records in the file. I setup my FDBatchMoveTextReader for CSV file adding the fields of the file. I Setup my FDBatchMoveDataSetWriter to write to the Dataset FDTableTask and assigned my table's fields to it.

Here is my basic code:

if OpenDialog1.Execute then
begin
  ShowMessage('Start read'); // more then 5 secods before this displays (sometimes)
  FDBatchMoveTextReader1.FileName := OpenDialog1.FileName;
  ShowMessage('Start Move'); // Dispalys instantly
  FDBatchMove1.Execute;
  Showmessage('done'); //About 25 seconds before this displays
end;

It works but takes very long. Doing this in Delphi 5 and using DBISAM and a CSV Import component, the whole process is like 5 seconds. I have just the default settings on the FDBatchMove component. Not only does it take long, but I am replacing the data in the FDTableTask table by setting the FDBatchMove1.Options to [poClearDest,poIdentityInsert].

The process is slow and the grid never gets repopulated with the new file data until I close and reopen the app. How can I make this process faster and display the new data in the grid when the process complete?

1 Answers1

1

Update In view of your comments, I've updated this a third time to show an example which is as close as I'm prepared to get to your code and which does not seem to suffer the problems you say you are having. I can only suggest that you try it yourself and then try and pin down why your own project does not behave in the same way. I am not going to spend any more time on this.

Using Live Bindings is way slower than using traditional db-aware components.

That said, I'm afraid I cannot reproduce your problems. I set up a multi-device FMX project as shown in the code and DFM extract below. To make it as self-contained as possible, I've put all the components and code in a single form unit and the code generates the CSV file to import.

As you'll see if you compile and run the project, the app starts with the StringGrid containing 3 rows, and clicking the ImportCSV button generates and imports 9997 extra rows. This takes no more than a couple of seconds on my laptop.

Note that I don't see any noticeable change in the speed of the ImportCSV procedure, if I comment out the calls to DisableControls and EnableControls. This surprised me slightly, but perhaps TFDBatchMove does this or similar internally.

This sample app shows a quirk of LiveBindings (in Seattle at least). Without the calls to FDMemTable1.First in FormCreate and ImportCSV, the StringGrid only shows row 3 after FormCreate and rows 1, 2 and 10000 after ImportCSV.

Code:

type
  TForm1 = class(TForm)
    FDGUIxWaitCursor1: TFDGUIxWaitCursor;
    StringGrid1: TStringGrid;
    BindSourceDB1: TBindSourceDB;
    BindingsList1: TBindingsList;
    FDMemTable1: TFDMemTable;
    FDMemTable1ID: TIntegerField;
    FDMemTable1Name: TStringField;
    LinkGridToDataSource1: TLinkGridToDataSource;
    FDBatchMove1: TFDBatchMove;
    FDBatchMoveDataSetWriter1: TFDBatchMoveDataSetWriter;
    Button1: TButton;
    BindNavigator1: TBindNavigator;
    FDBatchMoveTextReader1: TFDBatchMoveTextReader;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    procedure ImportCSV;
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.Button1Click(Sender: TObject);
begin
  ImportCSV;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FDMemTable1.IndexFieldNames := 'ID';
  FDMemTable1.CreateDataSet;
  FDMemTable1.InsertRecord([1, 'One']);
  FDMemTable1.InsertRecord([2, 'Two']);
  FDMemTable1.InsertRecord([3, 'Three']);
  FDMemTable1.First;
end;

procedure TForm1.ImportCSV;
var
  AFileName : String;
  TL : TStringList;
  i : Integer;
begin
  AFileName := 'c:\temp\book1.csv';

  try
    TL := TStringList.Create;
    for i := 4 to 10000 do
      TL.Add(IntToStr(i) + ',' + 'Row ' + IntToStr(i));

    TL.SaveToFile(AFileName);

    FDMemTable1.DisableControls;
    FDBatchMoveTextReader1.FileName := AFileName;
    FDBatchMove1.Execute;
    FDMemTable1.First;
  finally
    FDMemTable1.EnableControls;
    TL.Free;
  end;
end;

DFM

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 480
  ClientWidth = 429
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  OnCreate = FormCreate
  DesignerMasterStyle = 0
  object StringGrid1: TStringGrid
    Position.X = 8.000000000000000000
    Position.Y = 8.000000000000000000
    Size.Width = 409.000000000000000000
    Size.Height = 201.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 3
    RowCount = 100
    RowHeight = 21.000000000000000000
    Viewport.Width = 389.000000000000000000
    Viewport.Height = 176.000000000000000000
  end
  object Button1: TButton
    Position.X = 160.000000000000000000
    Position.Y = 288.000000000000000000
    TabOrder = 10
    Text = 'Button1'
    OnClick = Button1Click
  end
  object BindNavigator1: TBindNavigator
    Position.X = 8.000000000000000000
    Position.Y = 216.000000000000000000
    Size.Width = 240.000000000000000000
    Size.Height = 25.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 19
    DataSource = BindSourceDB1
    xRadius = 4.000000000000000000
    yRadius = 4.000000000000000000
  end
  object FDGUIxWaitCursor1: TFDGUIxWaitCursor
    Provider = 'FMX'
    Left = 352
    Top = 48
  end
  object BindSourceDB1: TBindSourceDB
    DataSet = FDMemTable1
    ScopeMappings = <>
    Left = 160
    Top = 48
  end
  object BindingsList1: TBindingsList
    Methods = <>
    OutputConverters = <>
    Left = 272
    Top = 48
    object LinkGridToDataSource1: TLinkGridToDataSource
      Category = 'Quick Bindings'
      DataSource = BindSourceDB1
      GridControl = StringGrid1
      Columns = <>
    end
  end
  object FDMemTable1: TFDMemTable
    FetchOptions.AssignedValues = [evMode]
    FetchOptions.Mode = fmAll
    ResourceOptions.AssignedValues = [rvSilentMode]
    ResourceOptions.SilentMode = True
    UpdateOptions.AssignedValues = [uvCheckRequired, uvAutoCommitUpdates]
    UpdateOptions.CheckRequired = False
    UpdateOptions.AutoCommitUpdates = True
    Left = 72
    Top = 48
    object FDMemTable1ID: TIntegerField
      FieldName = 'ID'
    end
    object FDMemTable1Name: TStringField
      FieldName = 'Name'
    end
  end
  object FDBatchMove1: TFDBatchMove
    Reader = FDBatchMoveTextReader1
    Writer = FDBatchMoveDataSetWriter1
    Mappings = <>
    LogFileName = 'c:\temp\Data.log'
    Left = 72
    Top = 136
  end
  object FDBatchMoveDataSetWriter1: TFDBatchMoveDataSetWriter
    DataSet = FDMemTable1
    Left = 352
    Top = 136
  end
  object FDBatchMoveTextReader1: TFDBatchMoveTextReader
    DataDef.Fields = <>
    Left = 192
    Top = 136
  end
end
MartynA
  • 30,454
  • 4
  • 32
  • 73
  • Thank you for your answer and I will give it a shot this morning. I really miss DBISAM with just std Database components like tables and source components, but I need an embedded database engine that will work on both Windows and OSX and Embarcadero recommended I use FireDac with IB (IBlite). – Robert Griffin realsol Oct 12 '16 at 16:20
  • OK. It is much faster, but the Grid never gets repopulated. I should have mentioned, since I am coming from Delphi5, I put all of my Data Components on a form that I named Datamodule1. The Grid is on the main form and the Live Designer Wizard, when completed created a BindingsList on the main form. here is my current code: – Robert Griffin realsol Oct 12 '16 at 16:29
  • procedure TMainForm.MenuItem2Click(Sender: TObject); begin With DataModule do begin Try BindSourceDB1.DataSet := Nil; FDTableTask.DisableControls; if OpenDialog1.Execute then begin ShowMessage('Start read'); FDBatchMoveTextReader1.FileName := OpenDialog1.FileName; ShowMessage('Start Move'); FDBatchMove1.Execute; End; Finally FDTableTask.EnableControls; BindSourceDB1.DataSet := FDTableTask; Showmessage('done'); end; end; end; – Robert Griffin realsol Oct 12 '16 at 16:31
  • Please be more careful. In one comment, you say your "form" is named "Datamodule**1**" but in your next comment you write "With DataModule". Which is it? In any case, is your Datamodule1 really a TForm descendant or a TDataModule one? It's a big difference, and TDataModule descendants cannot contain visual components like grids. – MartynA Oct 12 '16 at 16:37
  • Btw, I will try and add a full code example to my answer later this evening, in a couple of hours or so. – MartynA Oct 12 '16 at 16:40
  • I used a regular TForrm for my Data Components only because I am used to having a Datamodule coming from Delph 5. This is a Multi-Device app for Windows and Mac. I didn't see how I could create a DataModule, so used a form. I only do this to keep my main UI from being cluttered with nonvisible data components. The mainform is where the actual grid is. Always populates on application start, but never after I use Batchmove. – Robert Griffin realsol Oct 12 '16 at 17:33
  • Thanks, but I am creating my first Multi-Device-Application. I can't create a DataModule within the project for some reason. Searched for fixes but really didn't find anything. This is why I used a form and just called in DataModule. The great thing is it works faster now, just doesn't repopulate my Grid afterward which is on a separate form. I am new to Delphi 10.1. When I used the bound the Grid Visually with the Wizard. It worked but created a BindingList component on the form where the Grid Resides. How else would I bind it? Data Controls aren't available in a multi-device app. – Robert Griffin realsol Oct 13 '16 at 03:41
  • I can add a DataModule to a multi-device app by going to File | New | Other | Delphi Projects | Delphi files | DataModule. Borland introduced DataModules to provide an alternative to putting data storage components on Forms, so it would be v. surprising if they were not available to multi-device apps. In any case, if you can't use a DataModule for whatever reaseon, just use a second form instead, and just make sure the it's created in your Project File before your main form is. Anyway, please **try** the code in my answer[cont] – MartynA Oct 13 '16 at 06:33
  • because I don't want to go to the trouble of re-doing it as a FireDAC batch move. – MartynA Oct 13 '16 at 06:34
  • I will this morning. I will have to make the DataModule a Form though. This is the error returned when I try to create a DataModule in a FMX app, – Robert Griffin realsol Oct 13 '16 at 14:45
  • [basic link]http://stackoverflow.com/questions/40011841/how-to-create-a-datamodule-in-delphi-10-1) – Robert Griffin realsol Oct 13 '16 at 14:47
  • Thanks for the multi-device update. Your's works great. The only difference between my code and yours is I am using IBLite and the data components are on a different form (which should be fine). I even tried to disconnect the FileTaskList, then Reconnect it and reactivate the FDTable Task. Then I switched from a Grid to StringGrid but still after all those changes the grid doesn't repopulate until after the application is restarted. Thanks for you help. I will contact my IB and Delphi Rep's at Embarcadero and see if they have any ideas. – Robert Griffin realsol Oct 13 '16 at 17:22