Entwickler-Ecke

Algorithmen, Optimierung und Assembler - Algorithmus für Aggragatberechnung


noidic - Do 05.04.07 14:04
Titel: Algorithmus für Aggragatberechnung
Mahlzeit,

mal was zwischendurch, wenn mal wer Zeit hat.

Gegeben sei eine XML-Vorlage, die in etwa so aussieht:


XML-Daten
1:
2:
3:
4:
5:
6:
7:
  ...
  <columns>
    <column fieldname="bla" function="average" />
    <column fieldname="blubb" function="sum" />
    ...
  </columns>
  ...


Außerdem haben wir ein ClientDataset, welches für jeden column-Knoten aus dem XML eine Spalte enthält und mit einigen hundert oder tausend Datensätzen befüllt ist.

Nun geht es darum, die angegebene Aggregatfunktion auf die Spalte anzuwenden. Mögliche Funktionen sind average, sum, min, max. Es ist ein Minimum von 0 für jede Spalte vorauszusetzen.

Die erste Idee war, einfach eine 2. Abfrage zu bauen und den Datenbankserver die Aggregate berechnen zu lassen, das geht aber nicht, da nach der Abfrage, aber vor der Berechnung noch Datensätze konsolidiert werden.

Nun kommt meine zweite und aktuelle Idee zum tragen, die da wäre das Dataset für jede Spalte, die eine Aggregatfunktion hat, einmal komplett durchzulaufen und zu berechnen. Die Berechnungen sähen wie folgt aus:

max (min entsprechend andersrum):


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
function GetMax(ADataset : TClientDataSet; AFieldName : String) : double;
begin
  Result := 0;
  ADataset.first;
  while not ADataset.eof do
  begin
    if ADataSet.FieldByName(AFieldName).asFloat > Result then
      Result := ADataSet.FieldByName(AFieldName).asFloat;
    ADataset.next;
  end;
end;


sum:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
function GetSum(ADataset : TClientDataSet; AFieldName : String) : double;
begin
  Result := 0;
  ADataset.first;
  while not ADataset.eof do
  begin
    Result := Result + ADataSet.FieldByName(AFieldName).asFloat;
    ADataset.next;
  end;
end;


average:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
function GetAvg(ADataset : TClientDataSet; AFieldName : String) : double;
var
  cnt : Integer;
  sum : float;
begin
  Result := 0;
  cnt := 0;
  sum := 0;
  ADataset.first;
  while not ADataset.eof do
  begin
    if not ADataSet.FieldByName(AFieldName).isNull then
    begin
      inc(cnt);
      sum := sum + ADataSet.FieldByName(AFieldName).asFloat;
    end;
    ADataset.next;
  end;
  Result := sum / cnt;
end;


Auch wenns noch nicht umgesetzt ist, so weiss ich doch, dass es nicht besonders schnell sein wird.

Nun die Preisfrage :)

Hat wer eine Idee, die Berechnung schneller durchzuführen?

Viel Spass :)


BenBE - Do 05.04.07 21:22

Gehört zwar irgendwie mehr in den Bereich Datenbanken, aber hier mal'n kleiner Tipp:

Delphi besitzt bei den DB-omponenten die Möglichkeit das Result-Set zwischenzuspeichern (z.B. Temporär-Tabellen). Auf diese kannst Du problemlos deine Aggregat-Funktionen anwenden.

Ansonsten: Berechne einfach alle Aggregat-Funktionen als zusätzliche Spalten und werte nur die aus, die ausgewertet werden sollen ;-) (@DBler: Schlagt mich dafür jetzt nicht --- es funzt, auch wenn's Performance auf dem DBMS zieht wie Seuche!!!).


noidic - Di 10.04.07 08:19

Die erste Idee wäre mal zu testen.

Die zweite verstehe ich so, dass ich schon bei der Abfrage der Daten die Aggregate berechnen soll. Das geht nicht, da zu dem Zeitpunkt noch Zeilen im Ergebnis enthalten sind, die beim tatsächlichen Ergebnis nicht mehr enthalten sind und somit das Aggregat verfälschen würden.