[PHP] SQL-Script geht über 3000 Zeilen, trägt aber nur 1600 in die DB ein.

cppnap

Lt. Junior Grade
Registriert
Nov. 2008
Beiträge
487
Guten Tag,

ich habe ein PHP Script geschrieben, welches mir Produkte mit den dazugehörigen Kategorien von einem alten XT-Shop in einen neuen Magento-Shop ummappen soll.

Das Script an sich erfüllt seine Logik, allerdings werden beim INSERT-Befehl nicht alle Befehle ausgeführt.


PHP:
while ($row = $result->fetch_assoc()) {
        $sql = 'SELECT entity_id from catalog_product_entity WHERE sku="' . $row['products_model'] . '"';
        $sql_categorie = 'SELECT categories_id from products_to_categories WHERE products_id="' . $row['products_id'] . '"';
        $old_cat_id = $con->query($sql_categorie)->fetch_assoc()['categories_id'];
        $new_product_id = $con->query($sql)->fetch_assoc()['entity_id'];

        $sql = 'INSERT INTO catalog_category_product (product_id, category_id, uid) VALUES ("' . $new_product_id . '", "' . $old_cat_id . '", "' . $counter . '")';
        echo $sql . '<br/>';
        $counter++;
        $con->query($sql);
    }

Er geht hier über 3000 Zeilen drüber und soll in die Tabelle "catalog_category_product" das Mapping zwischen Produkt und Kategorie einfügen. der Echobefehl gibt mir alle 3000 Zeilen korrekt aus. In der SQL-Tabelle finden sich hinterher aber lediglich um die 1600 Zeilen wieder, es fehlt also fast die Hälfte.

Woran kann das liegen?

Vielen Dank schon mal :-)
 
Ich weiß nicht genau was du vorhast aber du solltest dir die Themen:
SQL Allgemein
SQL Join / Subqueries
SQL Prepared Statements
SQL Injection
anschauen.


Du kannst das ganze auch in einem Query machen, so ca.
PHP:
SELECT entity_id from catalog_product_entity WHERE sku="' . $row['products_model'] . '"'


"INSERT INTO catalog_category_product (product_id, category_id, uid) 
VALUES (
   (SELECT entity_id from catalog_product_entity WHERE sku='".$row['products_model']."'), 
   (SELECT categories_id from products_to_categories WHERE products_id='".$row['products_id']."'),
   '".$counter."')
Dann würde ich für die uid ein Increment nutzen (DB seitig)
Hast du über der schleife noch nen Query ?

zu deinem Problem, wahrscheinlich liefert einer deine SELECT Befehle keine oder keine validen Daten für dien Insert
 
Danke für deine Antwort, der Query kann definitiv eleganter geschrieben werden, das werde ich mir noch ansehen. Die Funktionalität sollte aber dennoch gehen.

Da ich mir alle der 3000 Insert Befehle als Echo ausgeben lasse, habe ich auch per Hand Produkte nachgeprüft, die ausgegeben wurden aber nie in der Datenbank auftauchen.

Kommt er evtl mit so vielen Insert-Befehlen in kurzer Zeit nicht klar?
 
Hast du einen ausgegebenen INSERT ohne php mal versucht (direkt) in die DB zu speilen?

Welche DMS nutzt du den ?
Aber eigentlich dürfte das jede schaffen, die eine nur schneller als die andere.

Versuch mal Errors ausgeben zulassen falls welche da sind. (solltest bei DB Aktionen eh immer ein TRY Catch nutzen)
PHP:
try {
  // Tabelle bla existiert nicht
  $query  = 'SELECT * FROM bla';
  if(!$mysqli -> query($query)) {
    throw new Exception($mysqli -> error);
  }
}
catch (Exception $e) {
  echo $e -> getMessage();
}
 
Vielleicht irgendein Schlüssel oder Constraint-Problem dessen Fehler du nicht abfängst? Du müsstest bei jeder Kommunikation mit der Datenbank im Nachgang schauen ob der Rückgabewert einen Fehler indiziert oder nicht.

Generell würde ich vllt. auch mal nen Blick in die PDO-Klasse werfen. Die kannst du dazu zwingen Exceptions zu werfen, das macht die Fehlersuche teils deutlich einfacher.

SQL Allgemein
SQL Join / Subqueries
SQL Prepared Statements
SQL Injection

Gerade das mit der Injection ist für ein einfacher Migrationsscript wohl eher weniger ein Thema. Auf nicht viel mehr anderes bezieht sich ja das Prepared Statement, da bei Perfomanceargumenten wieder nur entgegnet werden kann, dass es wohl eine einmalige Migration ist. Dass die SQL-Basics sitzen sollte will ich nicht bestreiten. ;)
 
Zuletzt bearbeitet:
Hast du einen ausgegebenen INSERT ohne php mal versucht (direkt) in die DB zu speilen?

Genau die Idee kam mir auch gerade, ich hab den INSERT Befehl rein kopiert von einem Produkt was gefehlt hat.
Tabelle lässt keine Inserts von bereits vorhandenen IDs zu - gefixed, jetzt gehts - danke :-)
 
cppnap schrieb:
Genau die Idee kam mir auch gerade, ich hab den INSERT Befehl rein kopiert von einem Produkt was gefehlt hat.
Tabelle lässt keine Inserts von bereits vorhandenen IDs zu - gefixed, jetzt gehts - danke :-)

Gerne ;)

Wie sdwaroc schon sagte schau dir auch mal PDO an oder z.B. http://www.dddbl.de/ damit Trennst du deinen Code von der DB.
Wenn du jetzt von MYSQL zu MS-SQL gehst musst du deinen Code anpassen. mit PDO nicht (nur einen anderen Treiber)

Wenn du mal Zeit hast schau dir Postgresql an.
Ist viel viel schneller und auch mächtiger.

Bei einem Letzten Projekt habe ich von MY-SQL auf Postgresql mirgiert und hatte in diesem Fall nur noch 1/70 der Query Zeiten.
 
Dein Problem war einfach, dass Dir keine Fehler / Warnungen angezeigt wurden.
Mit Try-Catch arbeiten und ERRMODE_EXCEPTION aktivieren.
Evtl auch noch den SQL Modus aus STRICT_ALL_TABLES setzen.
Kann dann so aussehen:

PHP:
<?php
    (
    // Dem MySQL-Treiber der PDO-Schnittstelle werden Attribute zugeordnet: Namen im UTF-8-Format und der SQL-Session-Modus
    PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8, SESSION SQL_MODE = STRICT_ALL_TABLES',
);

try {
    // Herstellen der Verbindung zum DB-Server
    $db_conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password, $options);
    /*
     * Ganz wichtig ist es den ATTR_ERRMODE auf ERRMODE_EXCEPTION zu setzen!
     * Sonst wirft die PDOException keine Fehler, wenn z.B. STRICT_ALL_TABLES als SESSION SQL_MODE gesetzt ist und man versucht
     * einen String einzufügen, der länder ist als das VARCHAR Feld in der Datenbank
     */
    $db_conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    echo '<p style="color: red;">Fehler: ' . htmlspecialchars($e->getMessage()) . '</p>';
}
 
Wenn du jetzt von MYSQL zu MS-SQL gehst musst du deinen Code anpassen. mit PDO nicht (nur einen anderen Treiber)
Naja das stimmt so nicht ganz. Auch mit der PDO musst du schauen welche Flags vom jeweiligen Treiber unterstützt werden. Nach Anpassung der Flags bleibt aber alles beim Alten. Mit den Emulated Prepared umgeht man meine ich auch das Problem bei Datenbanken, die nativ keine Prepared statement kennen.

Wenn du mal Zeit hast schau dir Postgresql an.
Oder bspw. MariaDB als echten MySQL-Fork. Gibt viele schöne, schnelle und gut dokumentierte Alternativen.
 
Danke für die Tipps und Ratschläge, werd ich mir zu Herzen nehmen und mir das in Ruhe mal ansehen.

Jetzt war erst mal wichtig, dass das Script überhaupt funktioniert hat :-)
 
Zurück
Oben