Verständnisfrage zu Laufzeiten

Sprint

Erfahrenes Mitglied
Hallo zusammen,

ich habe hier ein Laufzeitproblem bei einem Script und keine Ahnung, wie sich das erklärt.

Folgendes Script kopiert Daten um und sortiert sie neu:
PHP:
$sql="select kdnr, vorname, nachname, gebdat, gebort, ausweisnr, passnr, ablaufdatum, passablauf, dkpkz from kunden";
$erg=mysqli_query($mysqli,$sql);
$gf = array();
while($zeile = mysqli_fetch_array($erg, MYSQLI_ASSOC)){
    $reihung = 1;
    $gf[] = array('kdnr'=>$zeile['kdnr'], 'vorname'=>$zeile['vorname'], 'nachname'=>$zeile['nachname'], 'gebdat'=>$zeile['gebdat'], 'gebort'=>$zeile['gebort'], 'ausweisnr'=>$zeile['ausweisnr'],
                    'passnr'=>$zeile['passnr'], 'ablaufdatum'=>$zeile['ablaufdatum'], 'passablauf'=>$zeile['passablauf'], 'dkpkz'=>$zeile['dkpkz'], 'reihung'=>$reihung);
    $sqli = "select * from gfs where kdnr = '".$zeile['kdnr']."'";
    $ergi = mysqli_query($mysqli,$sqli);
    while($zeilei = mysqli_fetch_array($ergi, MYSQLI_ASSOC)){
        $reihung++;
        $zeilei['reihung'] = $reihung;
        $gf[] = $zeilei;
    }
}
$sqli = "truncate gfs";
$senderi = mysqli_query($mysqli, $sqli);

reset($gf);
foreach ($gf as $g){
    $sqli = "INSERT INTO gfs (kdnr, vorname, nachname, ausweis, ablauf, pass, passablauf, gebdat, gebort, dkpkz, reihung)
            VALUES ('".$g['kdnr']."', '".$g['vorname']."', '".$g['nachname']."', '".$g['ausweisnr']."', '".$g['ablaufdatum']."', '".$g['passnr']."', '".$g['passablauf']."', '".$g['gebdat']."', '".$g['gebort']."', '".$g['dkpkz']."', '".$g['reihung']."');";
    $senderi = mysqli_query($mysqli, $sqli);
}
Es werden Personendaten aus dem Kundenstamm und aus der Geschäftsführertabelle genommen und zum Schluß wieder in die leere Tabelle geschrieben. Dabei geht es um rund 26000 Datensätze in der Kundentabelle und ca 11000 in der GF Tabelle.
Wenn ich das Script so laufenlasse, bricht es nach 5 Minuten mit einem Laufzeitfehler ab. Ich habe dann das Script verdreifacht und die Nummernbereiche in kleiner 60000, 60-70000 und größer 70000 einschränkt:
PHP:
$sql="select kdnr, vorname, nachname, gebdat, gebort, ausweisnr, passnr, ablaufdatum, passablauf, dkpkz from kunden where kdnr < 60000";
$sqli = "delete from gfs where kdnr between kdnr < 60000";
Wenn ich die drei Teile in einem Script nacheinander durchlaufen lasse, komme ich auf eine Gesamtlaufzeit von knapp über zwei Minuten. Und das verstehe ich nicht. Ist das ein Problem des Speichers? Hat da jemand eine Idee?
 

ComFreek

Mod | @comfreek
Moderator
$gf[] = array('kdnr'=>$zeile['kdnr'], 'vorname'=>$zeile['vorname'], 'nachname'=>$zeile['nachname'], 'gebdat'=>$zeile['gebdat'], 'gebort'=>$zeile['gebort'], 'ausweisnr'=>$zeile['ausweisnr'], 'passnr'=>$zeile['passnr'], 'ablaufdatum'=>$zeile['ablaufdatum'], 'passablauf'=>$zeile['passablauf'], 'dkpkz'=>$zeile['dkpkz'], 'reihung'=>$reihung); $sqli = "select * from gfs where kdnr = '".$zeile['kdnr']."'";
Das Ganze kannst du übrigens durch $gf[] = $zeile ersetzen.

Dein ganzer Algorithmus sieht mir so aus, als würdest einen JOIN via PHP berechnen wollen. Warum benutzt du nicht einfach JOIN auf SQL-Seite?
 

Zvoni

Erfahrenes Mitglied
Ich versuche gerade zu verstehen, was du genau erreichen möchtest.

Jedes mal wenn ich SQL-Statements in einer Schleife sehe, krieg ich Kopfweh.....

EDIT ausserdem kapier ich dein "... und sortiert sie neu" nicht, da ich nirgends ein Order By sehe
 
Zuletzt bearbeitet:

Sprint

Erfahrenes Mitglied
Ich bin gerade dabei, ein komplett neues System für unsere Firma zu bauen. Es war früher so, daß die Daten des ersten GF in der Kundentabellen gespeichert waren. Das war für Jahre genug. Dann kamen weitere GF dazu, die eine separate Tabelle hatten. Für die neue Version werden jetzt die Daten aus der Kundentabelle umgelagert und in einem nächsten Schritt werden dann alle überflüssigen Felder entfernt.

Ein JOIN ist das hier meiner Meinung nach nicht. Vor allem passieren aber zwischendrin noch ein paar weitere Überprüfungen und Umformatierungen, die ich hier weggelassen habe weil sie für das eigentliche Problem uninteressant sind. Denn das ist einfach nur, warum EIN Durchlauf nach ca. 2/3 Der Datensätze in einen Timeout rennt, während die Aufteilung in DREI Durchläufe sehr viel schneller passiert. Hochgerechnet dürfte es wohl nur ein Drittel bis ein Viertel der Zeit brauchen.
 

Zvoni

Erfahrenes Mitglied
Muss ich widersprechen.
ComFreek hat recht. Das sieht definitiv nach nem JOIN aus.
Was ich nicht verstehe: Wieso nimmst du die Kundentabelle als Master?
In gfs stehen doch sowieso schon die GF's drin. Wieso gleichst du nochmal mit Kunden ab?
In gfs dürften keine Sätze stehen, welche nicht sowieso in Kunden sind, also ist der Abgleich doch völlig überflüssig.

Und dein "Reihung"-Zeug sieht mir nach nem Fall für ROW_NUMBER aus

EDIT: Ich verstehe auch nicht, wieso eine separate gfs-tabelle? Einfach Spalte im Personenstamm "IstGF" Ja/nein bzw. hochlaufende Zahl, und fertig.
Alle mit "0" sind "normale" Personen, alles grösser 0 sind GF's

EDIT2: MOMENT! Könntest sogar recht haben, dass es kein JOIN ist, aber dafür ein UNION.
Zeile 4-7 holt einen Satz aus Kunden und weist es gf[] zu, Zeile 8 holt die "anderen" Sätze aus gfs und weist es auch gf[] zu!
Ist ein UNION.
Würde sich aber auch per SQL lösen lassen können, inkl. deinem Reihung
Kannst du mal ein paar (anonymisierte) Datensätze aus Kunden und gfs hier zur Verfügung stellen? Am besten als CSV
 
Zuletzt bearbeitet:

Sprint

Erfahrenes Mitglied
Nein, wie in meinem letzten Post schon erwähnt, stehen in der Kunden und in der GF Tabelle unterschiedliche Personen. In der Kundentabelle steht der zum Zeitpunkt der Anlage aktuelle Geschäftsführer. In der GF alle weiteren Geschäftsführer. Darum ja auch die unterschiedliche Anzahl an Datensätzen.
In Zukunft sind in der Kundentabelle keine Personendaten mehr, sondern alle in der GF Tabelle.

Und wie auch schon erwähnt, finden zwischen dem Auslesen und dem Eintragen noch weitere Schritte statt, die ein Join nicht machen kann.
 

Zvoni

Erfahrenes Mitglied
OK, also ein UNION.
mit nem sauberen SQL kannst du dir Zeile4-15 aber dann sparen (es sei denn, eine der genannten "Prüfungen" findet dort dazwischen statt).

btw: Ist das ein One-Shot? Also eine einmalige Sache?
Weil dann verstehe ich nicht wieso du das als PHP-Skript programmierst. Ist ein klassischer Fall für ein DB-AdminTool (MySQL-Workbench, phpMyAdmin)
 

Sprint

Erfahrenes Mitglied
Richtig, da finden einige Prüfungen bzw. Formatierungen statt.

Auch richtig, das ist eine einmalige Sache. Es ist aber auch nur ein Teil eines viel umfangreicheren Scripts das gut ein Drittel aller Tabellen anfaßt. Und da ist auch so einiges drin, das über phpMyAdmin nicht zu machen ist.

Und gerade weil es eine einmalige Sache ist, muß es ja auch nur das tun was es soll und keinen Schönheitspreis gewinnen.
Und gerade weil es funktioniert, verstehe ich die ganze Diskussion nicht so wirklich, wenn es ja nur um gravierenden Unterschiede in der Laufzeit geht.
 

Zvoni

Erfahrenes Mitglied
Ahh, OK. Dann haste natürlich recht, und ich entschuldige mich für die "überflüssige" Diskussion.

Was ich rausfinden konnte: PHP scheint ein internes Time-Limit zu haben, was man abschalten kann
How to keep a php script from timing out because of a long mysql query
Frage: Weisst du an welcher Stelle der Time-Out kommt? Beim SELECT? Beim INSERT?

EDIT: Hier ein Beispiel, wie man anstatt ner INSERT-Schleife die VALUES in einen String baut, und dann anstatt einer Schleife ein einziges INSERT an den Server schickt (letzte Antwort)
PHP script with MySQL query timing out
 

Sprint

Erfahrenes Mitglied
Der Timeout kann eigentlich nur beim INSERT kommen, da er ja mit dem Schreiben erst anfängt, nachdem alle ca. 37000 Datensätze eingelesen sind.

Jetzt wo ich das so schreibe, kommt mir die Überlegung, daß es eigentlich nur am Speicher liegen kann, denn die Anzahl der Lese- und Schreibvorgänge sind ja in beiden Fällen identisch. Und es wird das Programm selbst nicht jedes Mal neu gestartet, sondern führt alle drei Durchgänge hintereinander aus.

Aber kann das einen Server mit 128 GB RAM und SSD so in die Knie zwingen?