Omdat ik een aantal .csv files heb die ik voor een hobby project wil inladen in een mySQL database heb ik een script gemaakt die de .csv files die op een vaste manier geformat zijn inleest en in bruikbare SQL uitspuugt. Dit script werkt met kleine bestanden prima.
Nu heb ik echter één .csv bestand van een paar honderd megabyte groot en als ik die door mijn script heen haal doet hij ongeveer een procent per uur, waardoor het omzetten dus in totaal ~100 uur gaat duren. Kortom mijn script is waarschijnlijk ‘een beetje’
inefficiënt.
Wat het in het kort doet is dat het het file per regel leest en de regel van comma gescheiden velden inleest in een array. Vanuit het array van regel 0 en de gelezen regel construeert het vervolgens een mySQL query per regel. Deze plakt hij allemaal aan elkaar vast in de string $output. Welke wanneer de loop helemaal doorlopen is wordt weggeschreven naar een .sql bestand en vervolgens geechoed.
De code als volgt:
Mijn vragen zijn:
Is er een betere methode dan de data eerst in een array inladen? Met een gewone find replace per regel gaat hij bijvoorbeeld niet goed om met comma’s die binnen ‘’ of “” staan.
Is het misschien slimmer om de variabele $output niet honderden mb’s groot te laten worden en per iteratie van de while loop de data al weg te schrijven? (ik denk het niet want dan moet het script steeds wachten op disk I/O). Of valt er eigenlijk niet veel te winnen als ik deze operatie per sé door php wil laten uitvoeren en moet ik mijn heil in een andere programmeertaal zoeken?
Nu heb ik echter één .csv bestand van een paar honderd megabyte groot en als ik die door mijn script heen haal doet hij ongeveer een procent per uur, waardoor het omzetten dus in totaal ~100 uur gaat duren. Kortom mijn script is waarschijnlijk ‘een beetje’
Wat het in het kort doet is dat het het file per regel leest en de regel van comma gescheiden velden inleest in een array. Vanuit het array van regel 0 en de gelezen regel construeert het vervolgens een mySQL query per regel. Deze plakt hij allemaal aan elkaar vast in de string $output. Welke wanneer de loop helemaal doorlopen is wordt weggeschreven naar een .sql bestand en vervolgens geechoed.
De code als volgt:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
| <?php set_time_limit(0); ini_set('memory_limit','1200M'); $linecount = 0; $filename = 'test1.csv'; $lineNumber = 0; $fields = NULL; $output = NULL; $tableName = $_GET['tableName']; if($tableName == NULL){ $tableName = 'test'; } $prevPercentage = 0; //tel het aantal regels $handle = fopen($filename, "r"); while(!feof($handle)){ $line = fgets($handle); $linecount++; } fclose($handle); //Loop het file regel voor regel af en maak er sql van, de eerste regel bevat de tabelstructuur $handle = fopen($filename, "r"); echo "Main loop starting...<br>"; if ($handle) { while (($line = fgets($handle)) !== false) { $lineArray = str_getcsv($line, ",", "\""); //Spuug een soort van percentage uit op basis van hoeveel regels er al gedaan zijn $countArray = count($lineArray); $a = 0; $percentage = ($lineNumber/$linecount)*100; if($percentage > $prevPercentage+1){ $prevPercentage = intval($percentage); echo intval($percentage)."% done... Program is still alive and kicking <br>"; } // de eerste regel bevat de tabelstructuur if($lineNumber == 0){ $fieldsStructured = NULL; while($a < $countArray){ if($a != 0){ $fieldsStructured = $fieldsStructured.", `".$lineArray[$a]."` text NULL"; $fields = $fields.", `".$lineArray[$a]."`"; } else { $fieldsStructured = "`".$lineArray[$a]."` text NULL"; $fields = "`".$lineArray[$a]."`"; } $a++; } $output = "DROP TABLE IF EXISTS `".$tableName."`;<br>CREATE TABLE IF NOT EXISTS `".$tableName."` (".$fieldsStructured.") ENGINE=InnoDB DEFAULT CHARSET=latin1;"; } else { // Niet de eerste regel dit is dus data $lineStructured = NULL; while($a < $countArray){ //verwijder bestaande " van de data $lineArray[$a] = str_replace("\"","", $lineArray[$a]); //escape alle andere dingen $lineArray[$a] = mysql_real_escape_string($lineArray[$a]); if($a != 0){ $lineStructured = $lineStructured.", '".$lineArray[$a]."'"; } else { $lineStructured = "'".$lineArray[$a]."'"; } $a++; } $output = $output."INSERT INTO `".$tableName."` (".$fields.") VALUES (".$lineStructured.");<br>"; } $lineNumber++; //zorg ervoor dat als er wat geëchoed wordt dit meteen zichtbaar is flush(); ob_flush(); } fclose($handle); } file_put_contents('output.sql', preg_replace('#<br\s*?/?>#i', "\n", $output)); echo $output; ?> |
Mijn vragen zijn:
Is er een betere methode dan de data eerst in een array inladen? Met een gewone find replace per regel gaat hij bijvoorbeeld niet goed om met comma’s die binnen ‘’ of “” staan.
Is het misschien slimmer om de variabele $output niet honderden mb’s groot te laten worden en per iteratie van de while loop de data al weg te schrijven? (ik denk het niet want dan moet het script steeds wachten op disk I/O). Of valt er eigenlijk niet veel te winnen als ik deze operatie per sé door php wil laten uitvoeren en moet ik mijn heil in een andere programmeertaal zoeken?
My Steam Profile (Name Switch) Worth: 889€ (225€ with sales)Games owned: 83