Brauche hilfe bei performance und /oder besseren Code

m.scatello

Erfahrenes Mitglied
Ich habe das gerade mal mit einer Datei mit 500 Einträgen ausprobiert, geht ruckzuck:
PHP:
<?php
   $fn = "ein_datei_name.txt";
 
   if (file_exists($fn))
   {
      $db = mysqli_connect("localhost", "root", "", "postleitzahlen");
      mysqli_set_charset($db, "utf8");
     
      $query = "INSERT INTO `data` (`fileid`, `name`, `gemeinde`, `plz`, `centr_lon`, `centr_lat`)
                VALUES (%d,'%s','%s','%s', %2.7f, %2.7f)";
               
      $content = file_get_contents($fn);
     
      $content = json_decode($content,true);
     
      $queries = array();
     
      foreach ($content as $entry)
      {
         $fileid    = mysqli_real_escape_string($db, $entry['id']);
         $name      = mysqli_real_escape_string($db, $entry['name']);
         $gemeinde  = mysqli_real_escape_string($db, $entry['gemeinde']);
         $plz       = mysqli_real_escape_string($db, $entry['plz']);
         $centr_lon = mysqli_real_escape_string($db, $entry['centr_lon']);
         $centr_lat = mysqli_real_escape_string($db, $entry['centr_lat']);
       
         $queries[] = sprintf($query, $fileid, $name, $gemeinde, $plz, $centr_lon, $centr_lat);
      }
     
      if (mysqli_multi_query($db, implode(";", $queries)))
        $output = "$fn erfolgreich importiert\n";
      else
        $output = "$fn *** IMPORT-FEHLER ***\n";
       
             
   }
   else
     $output = "$fn *** FEHLER DATEI NICHT VORHANDEN ***\n";
 
 
   $fp = fopen("import.log", "a+");
 
   fputs($fp, $output);    
 
   fclose($fp);
?>
 

basti1012

Erfahrenes Mitglied
Ich werde das mal testen.
Ich habe da ja schon viele Tage versuche gemacht ,rumgespielt usw.
Deswegen habe ich schon sehr viele Einträge in der Db.
Um doppelte Einträge zu vermeiden wollte ich ja mit SELECT erst abfragen ob der Eintrag schon vorhanden ist.

Hatte auch gedacht das man INSERT INTO auch mit IF NOT EXISTS und so verbinden könnte.
Versuche sahen so aus.

SQL:
$query="INSERT INTO `stadt_strassen_datenbank` (`fileid`, `name`, `gemeinde`, `plz`, `centr_lon`, `centr_lat`)
        SELECT  '$fileid','$name','$gemeinde','$plz','$centr_lon','$centr_lat'
        FROM `stadt_strassen_datenbank`
        WHERE NOT EXISTS (SELECT *
               FROM `stadt_strassen_datenbank`
               WHERE `fileid`='$fileid'
               AND   `name` = '$name')";

Ich denke mal das ich mit den einlesen nochmal von vorne anfange.
Da die Id's der Strassen wahrscheinlich einmalig sind , kann ich auf Select ja verzichten.
Wenn da doch eine Doppelt sein sollte laße ich es in einer Datei schreiben die man dann später per Hand kontrolliert.

Info nebenbei.
Die Hauptstrasse gibt es über 8000 mal.
Die Strasse Fi....tor nur 1 mal.

Das wahr Geografie, gute Nacht
 
Zuletzt bearbeitet:

basti1012

Erfahrenes Mitglied
So wollte mal bescheid geben.
Der Code aus post 11 sieht jetzt so aus
PHP:
<?php
/*
  Tabelle data

  CREATE TABLE `data` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`fileid` int(11) NOT NULL,
`name` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
`gemeinde` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
`plz` varchar(5) COLLATE utf8_unicode_ci NOT NULL,
`centr_lon` float NOT NULL,
`centr_lat` float NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
*/

$time_start = microtime(true);


$fp = fopen("import.log", "a+");

$files = glob("./txt/*.txt");

$db = mysqli_connect("localhost", "root", "", "postleitzahlen");
mysqli_set_charset($db, "utf8");

$query = "INSERT INTO `data` (`fileid`, `name`, `gemeinde`, `plz`, `centr_lon`, `centr_lat`)
           VALUES (%d,'%s','%s','%s', %2.7f, %2.7f)";

foreach ($files as $file)
{ 
      $content = file_get_contents($file);
    
      if (strlen ($content) > 2)
      { 
         $content = json_decode($content,true);
    
         $queries = array();
    
         foreach ($content as $entry)
         {
            $fileid    = mysqli_real_escape_string($db, $entry['id']);
            $name      = mysqli_real_escape_string($db, $entry['name']);
            $gemeinde  = mysqli_real_escape_string($db, $entry['gemeinde']);
            $plz       = mysqli_real_escape_string($db, $entry['plz']);
            $centr_lon = mysqli_real_escape_string($db, $entry['centr_lon']);
            $centr_lat = mysqli_real_escape_string($db, $entry['centr_lat']);
      
            $queries[] = sprintf($query, $fileid, $name, $gemeinde, $plz, $centr_lon, $centr_lat);
         }
    
         if (mysqli_multi_query($db, implode(";", $queries)))
         {
            $i = 0;
            while (mysqli_next_result($db)) $i++;
          
           $output = "$file erfolgreich importiert\n";
         }
         else
            $output = "$file *** IMPORT-FEHLER ***\n";
     }
     else
       $output = "$file empty\n";
 
     fputs($fp, $output);   
}

fclose($fp);

$time_end = microtime(true);

$time = $time_end - $time_start;

echo "Running $time seconds";

?>
Damit konnte ich 1.200.000 Einträge in 80 Sekunden in der Datenbank eintragen.
Wenn noch einer Ideen zu verbesserung hat dann bitte schreiben.
Ansonsten würde ich das so erstmal als Lösung stehen lassen.
 

Sempervivum

Erfahrenes Mitglied
Dieses fällt mir noch ein:
Da die Id's der Strassen wahrscheinlich einmalig sind , kann ich auf Select ja verzichten.
Damit keine mehrfachen Einträge entstehen, muss dann die ID unique sein. Möglicherweise hast Du das schon, oder Du hast vorher alles gelöscht, so dass es nicht akut ist.
 

ComFreek

Mod | @comfreek
Moderator
Dieses fällt mir noch ein:
Damit keine mehrfachen Einträge entstehen, muss dann die ID unique sein. Möglicherweise hast Du das schon, oder Du hast vorher alles gelöscht, so dass es nicht akut ist.
Beachte, dass wenn du die id in der DB als UNIQUE deklarierst, der Import sehr langsam werden kann. Hingegen wenn du zuerst importierst und dann die id als UNIQUE deklarierst, dann kann es bedeutend schneller sein -- allein wegen dem geringeren Overhead das einmal anstatt 1,2 Millionen mal zu machen.

@basti1012 Wie gesagt, ich würde Prepared Statements empfehlen. So muss das DBMS das SQL Query nur ein einziges Mal parsen und du brauchst auch kein mysqli_real_escape_string mehr. Escaping ist ja nur unnötiger Overhead, wenn du die Daten als solches direkt an das DBMS liefern kannst -- was Prepared Statements genau tun.
 

basti1012

Erfahrenes Mitglied
Die ID unique zu stellen hätte ich gemacht wenn ich die Datenbank so weiter geführt hätte wie am Anfang, wo ich noch mit Select und insert gearbeitet habe.
Ich hatte dann diesen Code laufen lassen und gesehen das es ohne probleme funktioniert hat.
Dann konnte ich die alte Datenbank löschen und gut ist.
Das gute wahr das hier kein Datensatz doppelt sein dürfte.
Wenn ich das Script noch mal laufen lassen würde ( ausversehen oder extra ) , dann würde alles doppelt sein.
Kann man eigentlich irgendwie bei der Datenbank einstellen das man jetzt nicht mehr ( ausversehen oder extra ) da noch mehr Einträge machen kann ?.

Ich muß zugeben das ich bis jetzt noch nie mit Prepared Statements gearbeitet habe.
Man kennt zwar die Vorteile aber ich habe es mal ohne gelernt und seid dem auch nicht dran gedacht das zu ändern.
Ich werde es aber mal versuchen.
 

ComFreek

Mod | @comfreek
Moderator
Kann man eigentlich irgendwie bei der Datenbank einstellen das man jetzt nicht mehr ( ausversehen oder extra ) da noch mehr Einträge machen kann ?.

Hingegen wenn du zuerst importierst und dann die id als UNIQUE deklarierst [...]

Also ja, siehe How add unique key to existing table (with non uniques rows). Möglicherweise willst du gleich einen PRIMARY KEY für deine id-Spalte. Dafür siehe How to add a primary key to a MySQL table?. Beide Links sind übrigens Tophits bei Google :)