"Christmas - the time to fix the computers of your loved ones" « Lord Wyrm

Massen PHP substr und MySQL Insert

Umlüx 11.04.2016 - 14:33 2071 7
Posts

Umlüx

Huge Metal Fan
Avatar
Registered: Jun 2001
Location: Kärnten
Posts: 8962
als folgefrage zu meinem CSV problemthread:

stellt euch vor, ihr müsst einen string in rund 90 (verschieden große) teile zerlegen und diese dann in eine MySQL tabelle schreiben.

ich würd jetzt hergehen und den string mit 90 "substr" in 90 variablen zersetzen und diese 90 variablen danach in ein insert packen. und das dreimal für je drei verschiedene dateien.

...gibts da irgendwelche eleganteren, advanced methoden mit weniger schreibaufwand? :D
die tabelle würde jedesmal truncated werden, also gibts vielleicht ja irgendwelche tricks um sie z.b. gleich autoamtisch aus einem array mit den 90 werten zu erstellen?
irgendwas, was weniger nach anfänger aussieht und mich weiterbildet?

danke!
Bearbeitet von Umlüx am 11.04.2016, 14:33

COLOSSUS

Administrator
GNUltra
Avatar
Registered: Dec 2000
Location: ~
Posts: 12067
Rede ich eigentlich gegen eine Wand?

Code:
$ printf '%20s%20s%20s%20s\n' 1 2 3 4 > data_input
$ printf '%20s%20s%20s%20s\n' blah blub blaer foobar >> data_input

Code:
$ cat data_input 
                   1                   2                   3                   4
                blah                blub               blaer              foobar

Code:
$ cat demo.php 
#!/usr/bin/env php5
<?php
$handle = @fopen("data_input", "r");
if ($handle) {
	while (($buffer = fgets($handle, 4096)) !== false) {
		$res = sscanf(trim($buffer), '%20s%20s%20s%20s', $a, $b, $c, $d);
		print "a=$a b=$b c=$c d=$d\n";
	}
	if (!feof($handle)) {
		echo "Error: unexpected fgets() fail\n";
	}
	fclose($handle);
}
?>

Code:
$ ./demo.php 
a=1 b=2 c=3 d=4
a=blah b=blub c=blaer d=foobar

(Disclaimer: Ich kann kein PHP.)

Achtung; der [code]-bbcode-Tag verschlingt Backslashes beim Rendern.

Umlüx

Huge Metal Fan
Avatar
Registered: Jun 2001
Location: Kärnten
Posts: 8962
ja ok die substr kann ich mit sscanf abkürzen, danke.
bleibt noch der insert in die db

Obermotz

Fünfzylindernazi
Avatar
Registered: Nov 2002
Location: OÖ/RI
Posts: 5262
Bulk Insert via
INSERT INTO table (a,b) VALUES (1,2), (2,3), (3,4);

Einfach mit prepared Statement: http://stackoverflow.com/a/12960624

Noch cooler:
https://dev.mysql.com/doc/refman/5.7/en/load-data.html
Bearbeitet von Obermotz am 11.04.2016, 15:53

Umlüx

Huge Metal Fan
Avatar
Registered: Jun 2001
Location: Kärnten
Posts: 8962
so, ich glaub ich hab jetzt halbwegs was wie ich mir das vorstelle.

hier ein gekürzter beispielstring aus einem der quellfiles:
Code:
$string = "01VSPRI                 ALLE     25.07.201625.07.2016";

erst bau ich mir ein array mit der datensatzstruktur. hier hab ich die feldbezeichnungen, startposition und länge und datentyp für die mysql. davon gibts 90 stück...
Code: PHP
$data = array ("version" => array(0,2,'int'),                
                "aktion" => array(2,1,'varchar'),
                "lieferant" => array(3,5,'varchar'),
                "angebotsnummer" => array(14,10,'int'),
                "benutzergruppe" => array(24,9,'varchar'),
                "angebotstermin" => array(33,10,'varchar'),
                "angebotsdauer" => array(43,10,'varchar')
               );

dann lass ich mir die tabelle in der mysql datenbank bauen. ich werde wohl einfach immer eine tabelle für jedes file und jeden tag anlegen und mir die letzten 3 tage behalten. der rest wird gedropt.
Code: PHP
$keys = array_keys($data);
$query = "DROP TABLE IF EXISTS flugdaten".date("Ymd").";
          CREATE TABLE flugdaten".date("Ymd")." ( 
                id mediumint(8)";
foreach($keys as $key) {
  $query .= ", $key ".$data[$key][2]."(".$data[$key][1].")";
}
$query .= ") ENGINE=MyISAM";


jetzt gehen wir das quellfile zeilenweise durch und zerlegen den string mit hilfe des definitionsarrays und bauen gleich das insert dazu
Code: PHP
$i=1;
$query = "INSERT INTO flugdaten".date("Ymd")." (id,".implode(",",$keys).") VALUES ( $i";
             
foreach($keys as $key) {
  // get data from string  
  $value = substr($string,$data[$key][0],$data[$key][1]);
  $query .=  ", '$value'";
}
$query .= ")";
$i++;

umständlich? wirkt so
gehts besser? bestimmt
dafür kaum schreibarbeit bis auf die erstellung des definitions arrays
Bearbeitet von Umlüx am 11.04.2016, 16:44

Crash Override

BOfH
Registered: Jun 2005
Location: Germany
Posts: 2951
Hast du einen fortlaufenden Key? Dann währe eine eine Tabelle mit partitionierung einfacher.
Für die Performance könnte es helfen, vor der Schleife ein "BEGIN;" abzusetzen und z.B. alle 100-1000 Schleifendurchläufe ein "COMMIT; BEGIN;" nach der Sleife dann noch ein "COMMIT;". Damit hast du nur eine Schreiboperation alle 100-1000 Einträge anstatt für jeden Eintrag.

Umlüx

Huge Metal Fan
Avatar
Registered: Jun 2001
Location: Kärnten
Posts: 8962
danke für den tipp.
performance könnte bei 2mio+ zeilen eine sache werden. ich behalts mir im hinterkopf

kleinerChemiker

Here to stay
Avatar
Registered: Feb 2002
Location: Wien
Posts: 4281
Entweder über LOAD DATA INFILE und davor das File anpassen, oder ansonsten würde ich auf alle Fälle mit Prepared Statements arbeiten.
Kontakt | Unser Forum | Über overclockers.at | Impressum | Datenschutz