dinsdag 19 augustus 2008

ADO = Traag, kan 2x sneller!

Ik had wel eens eerder gehoord dat ADO traag zou zijn, maar ik had met ADO nog geen ervaring.
(trouwens, deze traagheid geldt voor ADO Win32, ADO.net weet ik niet, maar als ik SQL Server Enterprise Manager 2003 (win32)
vergelijk met SQL Server Management Studio 2005 (.net) dan is de .Net versie VELE malen trager!)

Geen ervaring, tot nu toe, want bij mijn huidige opdracht hebben ze een aantal database performance problemen.
De performance problemen komen oa door een slecht ontwerp (elke cell in een grid/lijst is een object
met allerlei properties -> veel overhead), maar ook voor een deel door ADO. De data wordt namelijk
via ADO geladen in hun eigen data objecten.

Nu verloopt bij ADO alles via OLE interfaces. Elke call moet worden geconverteerd, gecontrolleerd, foutafhandeling ,etc.
Dit geeft veel overhead. En bij het laden van data gebeurt dit bij elke cell! Als je veel records met veel velden hebt, gaat het dus hard...
Om deze overhead te minimaliseren kun je zelf ook gebruik maken van de interfaces ipv bijv. de standaard ADO dataset van Delphi.
Dit kan het ongeveer 2x sneller maken! Vooral bij veel enkelvoudige queries (detail record van een ID ophalen).

Voorbeeld:

//eerst data laden
procedure LoadData(aSQL: string);
var
connection : TADOConnection;
recordset : _Recordset;
dataset : TADODataSet;
begin
//recordset bevat data van SQL statement (select * from bla)
recordset := connection.Execute( aSQL );

//of van (bestaande) dataset
dataset.CommandText := aSQL;
dataset.Open;
recordset := dataset.RecordSet;
end;

//dan data vullen in een data object oid
procedure FillData;
var
field : TField;
adofield : ADOInt.Field;
recordset : ADOInt._Recordset
i : integer;
olerows : OleVariant;
begin
for i := 0 to recordset.Fields.Count-1 do
begin
//store adofield reference
adofield := recordset.Fields[i];
//get field object by name
f := data.FieldByName( adofield.Name );

//get value
f.Value := adofield.Value;

//of
//vanaf ADO 2.5 30% sneller?
f.Value := recordset.Collect[i];

end;

//of: alle waarden van record (of meerdere regels) in een array:
//get all values of current row
olerows := recordset.GetRows(1, 0, EmptyParam);

for i := 0 to recordset.Fields.Count-1 do
//niet sneller maar iets trager?
f.Value := olerows[i, 0];
end;

PS: eventueel de waarde van OleVariant nog omzetten naar juiste type Variant dmv "VarAsType", want met
OLE kan bijvoorbeeld een datum of een decimaal als een intern type opgeslagen zijn.