If you want to really make your application scream, then one key area to get your interaction right is with the database layer. The example below becomes 8 (eight) times faster processing 10,000 records!
A number of high end databases (including Oracle and InterBase) include support for array inserts which, in short, is a much faster way to insert data. Rather than having to process record after record, the process accepts a block of changes that are processed in one hit. To be clear, this is not the same as starting and ending a database transaction, (something else that can also really help with speed).
Array DML in FireDAC helps you take advantage of database engines that support this feature easily. To see the difference in code and speed, lets explore a simple example. The example uses the following table called test containing two fields, Integer and string
CREATE TABLE test(Field1 INTEGER, field2 VARCHAR(20))
The typical way to insert 10000 records is to use a loop, where we have a query that just updates the parameters and executes the SQL.
const NUM_INSERTS = 10000; procedure TForm7.Button1Click(Sender: TObject); var i: integer; t: integer; begin FDQuery1.ExecSQL('DELETE FROM test'); FDQuery1.SQL.Text := 'INSERT INTO test(field1, field2) values (:field1, :field2)'; t := GetTickCount; FDQuery1.Connection.StartTransaction; try for i := 0 to NUM_INSERTS-1 do begin FDQuery1.Params[0].AsInteger := i; FDQuery1.Params[1].AsString := 'Str' + IntToStr(i); FDQuery1.ExecSQL; end; finally FDQuery1.Connection.Commit; end; (Sender as TButton).Caption := FloatToStr((GetTickCount() - t) / 1000); end;
To alter this code to use Array DML, there is very little to do. Firstly, before setting the parameter values you can set the size of the array you want to insert. (You can always set this higher that needed if your not sure)
Once the array size is set, there is a small modification to the way the parameter values are set. FireDAC supports the plural setters for setting each record in the dynamic array. (see code below)
Finally, there execute command is changed to tell it to run for the total records you want to insert.
procedure TForm7.Button2Click(Sender: TObject); var i: integer; t: integer; begin FDQuery1.ExecSQL('DELETE FROM test'); FDQuery1.SQL.Text := 'INSERT INTO test(field1, field2) values (:field1, :field2)'; t := GetTickCount; FDQuery1.Connection.StartTransaction; try FDQuery1.Params.ArraySize := NUM_INSERTS; for i := 0 to NUM_INSERTS-1 do begin FDQuery1.Params[0].AsIntegers[i] := i; FDQuery1.Params[1].AsStrings[i] := 'Str' + IntToStr(i); end; FDQuery1.Execute(NUM_INSERTS,0); finally FDQuery1.Connection.Commit; end; (Sender as TButton).Caption := FloatToStr((GetTickCount() - t) / 1000); end;
So what is the difference at run time? On my PC (virtual machine), running the 10,000 inserts takes 1.014 seconds in the first block of code, compared with 0.125 seconds with the second block of code. i.e. only 12% of the original processing time and over 8 times faster!
Thats about it! The code for the above Array DML FireDAC demo is available at code central http://cc.embarcadero.com/item/29832
An video example of this in action, along with a number of other FireDAC capabilities is also available in the FireDAC Session from Developer Direct Season 4 video.
Hey Stephen, I’ve had the pleasure of using ArrayDML. It’s an amazing tech. However, we recently migrated our datacenter to Azure and my ArrayDML utility no longer works. Do you have any insight as to what may be the cause? Using RAD Studio 10.2.3 with SQL Server 13.0.4446.0 in Azure (SQL Server installed in a VM, not as storage tables or blob storage). I also have SQL Server 13.0.4001.0 installed in our OnPrem data center and it works just fine. Is there a server setting that’s missing in SQL Server in Azure or a database setting that’s missing, etc.? Thanks for your input in advance.
Kelly.
Sorry, I’m not a SQL Server configuration expert.