Code: Alles auswählen.
REPORT ZTEST4.
TYPES: BEGIN OF TYPE_O,
ORGEH TYPE SOBID,
END OF TYPE_O,
BEGIN OF PUFFER,
ORGEH TYPE SOBID,
PLANS TYPE HROBJID,
END OF PUFFER.
DATA: O TYPE SORTED TABLE OF TYPE_O WITH UNIQUE KEY ORGEH WITH HEADER LINE,
S TYPE SORTED TABLE OF HROBJID WITH UNIQUE KEY TABLE_LINE,
I TYPE I,
I2 TYPE I,
PUFFER TYPE SORTED TABLE OF PUFFER WITH NON-UNIQUE KEY ORGEH PLANS WITH HEADER LINE.
*** START-OF-SELECTION ***
START-OF-SELECTION.
SELECT DISTINCT SOBID INTO O-ORGEH FROM HRP1001
WHERE OTYPE = 'S'
AND RELAT = '003'
AND RSIGN = 'A'
AND SCLAS = 'O'.
I = SWITCH #( I WHEN 0 THEN 1
WHEN 1 THEN 2
WHEN 2 THEN 0 ).
CHECK I = 0.
INSERT TABLE O.
ENDSELECT.
WRITE: / LINES( O ), 'Zeilen in der Tabelle O'.
* Teilpufferung
GET RUN TIME FIELD I.
SELECT SOBID OBJID INTO TABLE PUFFER FROM HRP1001
FOR ALL ENTRIES IN O
WHERE SOBID = O-ORGEH " Datenbanktabelle hat Sekundärindex SOBID/SCLAS/SUBTY/PLVAR/ENDDA
AND SCLAS = 'O'
AND SUBTY = 'A003'
AND PLVAR = '01'
AND ENDDA >= '20071511'
AND ISTAT = '1'
AND OTYPE = 'S'.
GET RUN TIME FIELD I2.
I = I2 - I.
I2 = LINES( PUFFER ).
WRITE: / 'Teilpufferung: Dauer', I, 'Zeilen:', I2.
* Komplettpufferung
REFRESH PUFFER.
GET RUN TIME FIELD I.
SELECT SOBID OBJID INTO TABLE PUFFER FROM HRP1001
WHERE OTYPE = 'S' " Datenbanktabelle hat Sekundärindex MANDT, OTYPE, RELAT, RSIGN, SCLAS
AND RELAT = '003'
AND RSIGN = 'A'
AND SCLAS = 'O'
AND PLVAR = '01'
AND ISTAT = '1'
AND ENDDA >= '20071511'.
GET RUN TIME FIELD I2.
I = I2 - I.
I2 = LINES( PUFFER ).
WRITE: / 'Komplettpufferung: Dauer', I, 'Zeilen:', I2.
* gar keine Pufferung
REFRESH PUFFER.
GET RUN TIME FIELD I.
LOOP AT O.
SELECT SOBID OBJID INTO PUFFER FROM HRP1001
WHERE SOBID = O-ORGEH " Datenbanktabelle hat Sekundärindex SOBID/SCLAS/SUBTY/PLVAR/ENDDA
AND SCLAS = 'O'
AND SUBTY = 'A003'
AND PLVAR = '01'
AND ENDDA >= '20071511'
AND ISTAT = '1'
AND OTYPE = 'S'.
INSERT TABLE PUFFER. " unter Beachtung des Sortierschlüssels einfügen
ENDSELECT.
ENDLOOP.
GET RUN TIME FIELD I2.
I = I2 - I.
I2 = LINES( PUFFER ).
WRITE: / 'Gar keine Pufferung: Dauer', I, 'Zeilen:', I2.
Folgende Benutzer bedankten sich beim Autor wreichelt für den Beitrag:
DeathAndPain
Doch, das habe ich durchaus im Auge gehabt. Deshalb habe ich das Programm mehrfach hintereinander ausgeführt, um mir anzuschauen, wie sich die Ergebnisse dann verhalten, und auch die Reihenfolge der drei Varianten vertauscht (damit nicht die, die später drankommen, von der Datenbankpufferung der vorhergehenden profitieren und sich dadurch das Ergebnis verfälscht). Von daher kann ich sagen, dass Deine Einwände nicht gültig sind, jedenfalls nicht für die Sybase-Datenbank. Wie es mit Oracle aussieht, vermag ich natürlich nicht zu sagen.Dein Benchmark ist so nicht aussagekräftig, weil nach dem ersten Select die Datenbank die Daten im Cache hat und das reduziert die Zugriffszeit für nachfolgende vergleichbarer Selects ganz enorm.
Wie mein Testergebnis zeigt, lohnt sich die Pufferung schon bei weit geringeren Zugriffszahlen. Jedenfalls relativ zum SELECT SINGLE. Du kannst natürlich argumentieren, dass absolut betrachtet die Zugriffszeit von SELECT SINGLE dann auch noch so gering ist, dass die Programmierung der Pufferung den Aufwand nicht lohnt. Das ist Geschmacksache - ich bin Perfektionist und habe noch die alte Faustregel im Kopf, dass ein Anwender ein System ab einer Sekunde Antwortzeit als unangenehm träge empfindet. Ich wünschte, die SAP wüsste das auch noch, vor allem bei dem ganzen HTTP-Geraffel (Fiori etc.). Dann würde das SAPGui vielleicht wieder in der Gunst steigen.Ein paar „Select SINGLES“ spielen keine Rolle. Aber so ab ca. 1.000 kann sich eine Pufferung in internen Tabellen lohnen.
Das war auch meine Vermutung, doch zumindest soweit es den IN-Operator betrifft, der ja letztlich auch so etwas ähnliches macht wie ein mehrfacher SELECT SINGLE, hat mein Testergebnis diese Vermutung eindrucksvoll widerlegt.Auch wenn man mit einem „Select INTO TABLE“ mehr Einträge liest, als man Anzahl „Select Singles“ hat, kann man da enorme Performancegewinne erzielen. Denn ein einzelner sequentieller Select mit passendem Schlüsselzugriff, wird von der DB viel effizienter erledigt, als viele einzelen „Select Singles“.
Ich rechne damit, dass heutzutage die Umsetzung von "FOR ALL ENTRIES" in den IN-Operator nicht nur bei Sybase der übliche Standard ist, auch wenn Sybase mit 128 eine überdurchschnittlich hohe empfohlene Blockgröße hat.„Select .. for all entries“ ist nicht schlecht, aber in den meisten Fällen sind das auch nur „Select Singles“ und eben ein implizierte DISTINCT
Den sollte man nicht überbewerten; das bringt nämlich nur was bei Tabellen, bei denen die Pufferung überhaupt aktiv ist. Bei großen Tabellen wie der HRP1001 oder der MARA ist die SAP-Tabellenpufferung ohnehin abgeschaltet. Da cachet allenfalls noch die Datenbank, und davon haste auch bei DISTINCT was.und eben ein implizierte DISTINCT und damit auf jeden Fall am SAP-Tabellenpuffer vorbei.
Mit solchen Wetten wäre ich vorsichtig! Ich habe gelernt, dass vieles, von dem man denkt, es müsste unglaublich langsam sein, weil es kompliziert ist, nicht so ist. Teilweise sind so ausgetüftelte Techniken im Hintergrund, da kommt man mit "gesundem Menschenverstand" nicht gegen an.DeathAndPain hat geschrieben: Im übrigen wette ich, dass eine Pufferung in eine eigene interne Tabelle, die nur aus den benötigten Feldern besteht und genau nach dem Schlüssel sortiert ist, nach dem man später zu lesen gedenkt, noch mal ein ganzes Stück schneller ist als ein SELECT-Befehl, den der ABAP-Interpreter abfangen und auf einen Tabellenpufferzugriff umsetzen muss. Wieviel das ausmacht, wäre mal ein weiteres Testprojekt.
Ja, aber die SAP-Optimierer kochen auch nur mit Wasser. Egal wie sie puffern, sie müssen hinterher über einen passenden Schlüssel suchen, und nichts anderes mache ich auch, nur dass ich klar im Vorteil bin, a) weil ich meine Puffertabelle nur mit den tatsächlich benötigten Zeilen fülle und b) weil ich meinen Sortierschlüssel viel optimaler wählen kann als der Datenbankoptimierer. Der kann ja im Prinzip nicht viel mehr machen, als nach einem Datenbankindex sortieren (mutmaßlich dem, über den der Zugriff stattgefunden hat), wohingegen ich mir nicht nur das Feld MANDT schon mal sparen kann, sondern auch einen Index nehme, der genau zu meinem Zugriffsmuster passt. Der muss mit den vorhandenen Datenbankindizes gar nichts zu tun haben. Wenn da der SAP-Puffer schneller wäre, dann würde das im Umkehrschluss bedeuten, dass die Verwaltung der internen Tabellen durch ABAP schlampert umgesetzt ist. Und das kann ich mir nicht vorstellen, dazu sind die zu wichtig (was die SAP ja auch mit dem hohem Aufwand an verfügbaren Befehlen und Sortiermechanismen unterstreicht).Mit solchen Wetten wäre ich vorsichtig! Ich habe gelernt, dass vieles, von dem man denkt, es müsste unglaublich langsam sein, weil es kompliziert ist, nicht so ist. Teilweise sind so ausgetüftelte Techniken im Hintergrund, da kommt man mit "gesundem Menschenverstand" nicht gegen an.
XML-Transformationen zum Beispiel gehen so dermaßen schnell, das bekommt nicht ansatzweise so nachprogrammiert.
Deswegen habe ich, wie gesagt, die Reihenfolge der Testzugriffe im Code probehalber vertauscht und mir angeschaut, wie sich das auf das Ergebnis auswirkt. Der Unterschied war gering und hatte auf die grundsätzliche Aussage des Ergebnisses keinen Einfluss.Bei deinem Beispiel könnte übrigens noch eine Rolle spielen, dass die PUFFER-Tabelle nur beim ersten SELECT allokiert werden muss. Bei den nachfolgenden SELECTS wurde der Speicherbereich bereits zur Verfügung gestellt.
Nein, denn ich will realitätsnah bleiben. In meinen Programmen kommen so große interne Tabellen so gut wie nicht vor; ich brauche nur ein paar tausend Zeilen. Die aber aus einer ganzen Reihe von Datenbanktabellen, so dass es sich läppert. Wenn ich da ein vernünftiges Ergebnis haben will, muss ich also testen, wie man bei dieser Größenordnung am besten optimiert, anstatt eine viel größere anzusetzen, die in meinen echten Programmen keine Rolle spielt.200.000 Zeilen sind auch keine Größe mehr für heutige Systeme. Für einen Performancetest würde ich deutlich größere Datenmengen verwenden.
Ganz genau. Da erzählt jeder was anderes, und auch was in einer Doku oder einem Hinweis steht, muss noch lange nicht stimmen oder könnte veraltet sein oder nicht zur eigenen Datenbank oder den eigenen Systemparametern passen. Das ist wie mit Motoröl beim Auto; Ölkunde ist nicht Bestandteil der Kfz-Mechanikerausbildung (auch nicht beim Meister), und daher wissen die Werkstätten nur, was per Mundpropaganda von einem zum nächsten getragen oder von Vertretern der Motorölfirmen, deren Priorität auf ihrem Absatz liegt, bei Werbebesuchen kolportiert wird. Dementsprechend desaströs ist, was der durchschnittliche Autobesitzer einfüllt. Die Werkstätten machen es nicht viel besser und halten sich als Untergrenze nur an die vom Autohersteller vorgeschriebenen Normen (die aber in vielen Fällen keineswegs optimal für das Fahrzeug sind).Insgesamt ist das Thema DB extrem komplex und mit viel Halbwissen behaftet (insbesondere bei mir... ).
Aber die Technik, mit der man das Wasser zum Kochen bringt, kann sich durchaus unterscheiden.DeathAndPain hat geschrieben: Ja, aber die SAP-Optimierer kochen auch nur mit Wasser.
Sorry. Das hatte ich überlesen.DeathAndPain hat geschrieben:Deswegen habe ich, wie gesagt, die Reihenfolge der Testzugriffe im Code probehalber vertauscht und mir angeschaut, wie sich das auf das Ergebnis auswirkt. Der Unterschied war gering und hatte auf die grundsätzliche Aussage des Ergebnisses keinen Einfluss.ewx hat geschrieben:Bei deinem Beispiel könnte übrigens noch eine Rolle spielen, dass die PUFFER-Tabelle nur beim ersten SELECT allokiert werden muss. Bei den nachfolgenden SELECTS wurde der Speicherbereich bereits zur Verfügung gestellt.
Das ist ein Argument.DeathAndPain hat geschrieben:Nein, denn ich will realitätsnah bleiben. In meinen Programmen kommen so große interne Tabellen so gut wie nicht vor; ich brauche nur ein paar tausend Zeilen. Die aber aus einer ganzen Reihe von Datenbanktabellen, so dass es sich läppert. Wenn ich da ein vernünftiges Ergebnis haben will, muss ich also testen, wie man bei dieser Größenordnung am besten optimiert, anstatt eine viel größere anzusetzen, die in meinen echten Programmen keine Rolle spielt.ewx hat geschrieben:200.000 Zeilen sind auch keine Größe mehr für heutige Systeme. Für einen Performancetest würde ich deutlich größere Datenmengen verwenden.
Solche Auswertungen könnte man evtl. auch regelmäßig laufen lassen um zum Beispiel schnell erkennen zu können, welchen Einfluß der größere Hausptspeicher oder neue Prozessoren oder ein DB-Update haben.DeathAndPain hat geschrieben: Deshalb teste ich selber ein bisschen rum; dann habe ich reale Zahlen in der Hand. Nur so konnte ich z.B. nachweisen, dass die HASHED-Tabellen (wie oben geschildert) nichts taugen. Das steht sonst nirgends geschrieben.
Überdies habe ich auch nicht den Anspruch genau vorherzuwissen, wieviel Zeilen ich in Zukunft brauchen werde. Das wird sich auch von Programm zu Programm und von Tabelle zu Tabelle unterscheiden. Mir reicht es, wenn die Größenordnung stimmt. Wenn ich statt einem Jahr zwei auswerte, dann lese ich bei einer Tabelle vielleicht 12.000 statt 6.000 Zeilen. Damit bin ich aber immer noch nicht im Bereich von 200.000 Zeilen; das ist eine ganz andere Kategorie.Es ist allerdings manchmal gefährlich, von bestimmten Annahmen auszugehen. Von einem Monat auf den anderen kommt ein Buchungskreis hinzu oder es wird nicht nur ein Jahr ausgewertet, sondern zwei etc.
Insofern ist es aber auch wieder sinnvoll, sich auf die bekannte realistische Größe zu konzentrieren, denn es ist kaum sinnvoll, ein Programm tagelang für eine vielleicht in der Zukunft auftretende Datenkonstellation zu optimieren.
Das ist so.In jedem Fall merkt man, sobald man sich mit dem Thema Performance beschäftigt, wie viele Rädchen da ineinander greifen und sich gegenseitig bedingen.
Die hängen auch von ziemlich vielen Parametern ab...DeathAndPain hat geschrieben:Dementsprechend habe ich auch nicht den Anspruch, mit meinen Tests ganz genaue Aussagen machen zu können.