Matthias: Performance bei Textzerlegung

Beitrag lesen

Miss bitte, wieviel Zeit Du gewinnst, wenn Du Deine Datei auf einen Rutsch schreibst. Ermittle den Flaschenhals. Ich gehe davon aus, dass Du dem Skript genügend Speicher zur Verfügung stellen kannst, dass es praktisch alle Operationen im RAM durchführen kann. Ständige Lese- oder Schreiboperationen auf der Festplatte bremsen ein Skript genauso aus wie unnötig viele DB-Operationen.

Mmmh.. wäre das nicht ein Schritt zurück? Ich hatte zuerst die Option, dass die Datei mit file() und dann einer foreach-Schleife abgearbeitet wurde. Dabei findet der Lesevorgang von der Platte einmal statt, dann arbeite ich mit den gelesenen Daten.
Das war aber im Vergleich zu fopen > while !feof > $zeile = fgets (was, wenn ich nichts falsch verstehe, wie von Dir geschildert konstant die Platte bemüht) 2 Sekunden schneller.

Haben die Multi-Insert-Anweisungen etwas gebracht? Wenn ja, wieviel?

Ja.

Damit dauert die Datei nun (bei 2.460 Zeilen) statt 6 Sekunden (jede Zeile ein eigenes Insert) 5 Sekunden (300er Insert-Packen).
Damit dauert die Datei nun (bei 6.844 Zeilen) statt 25 Sekunden (jede Zeile ein eigenes Insert) 11 Sekunden (300er Insert-Packen).

Prepared Statements haben bei der Ausführung in einer Schleife den Vorteil, dass das SQL-Statement nur ein einziges Mal "übersetzt" werden muss. Dadurch sollte sich bei genau solchen Anwendungsbeispielen wie Deinem ein Performancegewinn erzielen lassen.

Mit den prepared-Statements habe ich mich heute morgen kurz beschäftigt, habe damit aber glaube ich ein prinzipielles Verständnisproblem.

Ich habe folgenden Code gebaut, der auch funktioniert:

  
/* Set up the Transact-SQL query. */  
$tsql     =  "INSERT INTO  "  
            ."  table_name "  
            ."  (          "  
            ."    id,      "  
            ."    FieldA,  "  
            ."    FieldB,  "  
            [...]  
            ."    FieldM   "  
            ."  )          "  
            ."VALUES       "  
            ."  (          "  
            ."    ( ?),    "  
            ."    ( ?),    "  
            ."    ( ?),    "  
            [...]  
            ."    ( ?),    "  
            ."  )          ";  
  
$handle = fopen ( $this->datei_path.$this->datei_name , "r" );  
$nr     = '0';  
while ( ! feof( $handle ) )  
{  
  $nr++;  
  $zeile = fgets( $handle, '4096' );  
  //Leerzeilen abfangen  
  if ( strlen ( trim( $zeile ) ) > '0' )  
  {  
    $A =               substr( $zeile ,   '0' ,  '8' );  
    $B =        ltrim( substr( $zeile ,   '8' , '10' ) , '0' );  
    $C =        ltrim( substr( $zeile ,  '18' , '12' ) , '0' );  
    $D =        ltrim( substr( $zeile ,  '30' , '10' ) , '0' );  
    $E =        ltrim( substr( $zeile ,  '40' , '12' ) , '0' );  
    $F =        ltrim( substr( $zeile ,  '52' , '10' ) , '0' );  
    $G = strtr( ltrim( substr( $zeile ,  '62' , '15' ) , '0' ) , ',' , '.' );  
    $H =         trim( substr( $zeile ,  '77' , ' 1' )       );  
    $I =        rtrim( substr( $zeile ,  '79' ,  '9' ) , ' ' );  
    $J =         trim( substr( $zeile ,  '88' , '10' )       );  
    $K =         trim( substr( $zeile ,  '99' , '20' )       );  
    $L =         trim( substr( $zeile , '120' ,  '8' )       );  
    $M =         trim( substr( $zeile , '128' , '20' )       );  
  
    /* Set up the parameters array. Parameters correspond, in order, to  
       question marks in $tsql. */  
    $params = array( $nr,$BLDAT,$SKOST,$SAUFN,$EKOST,$EAUFN,$KSTAR,$IKPSU,$minus,$anwen,$kennz,$fatxt,$hitxt,$abrbr );  
  
    /* Create the statement. */  
    $stmt = sqlsrv_prepare( $this->DB_Connector, $tsql, $params);  
    if( $stmt ){}  
    else  
    {  
      echo "<br><br>Error in preparing statement.\n";  
      die( print_r( sqlsrv_errors(), true));  
    }  
  
    /* Execute the statement. Display any errors that occur. */  
    if( sqlsrv_execute( $stmt)){}  
    else  
    {  
      echo "<br><br>Error in executing statement.\n";  
      die( print_r( sqlsrv_errors(), true));  
    }  
  }  
}  
fclose ($handle);  

Das hat länger gedauert, und macht, wenn ich nicht irgendwas falsh verstehe, keinen Sinn.
Mit dieser Version erzeuge ich statt einem Insert-Befehl pro Zeile nun einen PrepareStatement-Befehl und einen ExecuteStatement Befehl.
Mehr Sinn macht es in meinen Augen, wenn ich den sqlsrv_prepare-Befehl vor die Schleife setze. Dann ist nur das Problem, dass er den $params-array noch nicht kennt.
Also an dem Zusammenspiel von Schleife und prepare-Insert knabbere ich noch...

Welches DBMS setzt Du ein?

Ich greife per SQLManager Lite auf eine MsSql-Db zu. Die DB selbst wird aber von unserer IS verwaltet, was die nutzen müsste ich nachfragen.

Freundliche Grüße

Matthias