I have mysql database and I want to insert about 40'000 rows into it from PHP code , but my code takes more than 15 minutes to insert the rows, is there any chances to optimize it? where is my problem(PHP code / database design) ?
here are the details:
- the row data are stored in a utf-8 txt file the values separated by "\t" tab character and every row sets in one line of the file, like this
string view:
"value1\tvalue2\tvalue3\value4\value5\r\nvalue1\tvalue2\tvalue3\value4\value5\r\nvalue1\tvalue2\tvalue3\value4\value5\r\nvalue1\tvalue2\tvalue3\value4\value5\r\n"
text reader view:
value1 value2 value3 value4 value5
value1 value2 value3 value4 value5
value1 value2 value3 value4 value5
value1 value2 value3 value4 value5
-the data base has 3 tables as this:
table1 countries fields(1) (NAME varchar -primarykey-)
table2 products fields(2) (HS varchar - primarykey-, NAME varchar)
table3 imports fields (6) (product_hs varchar -foreignkey->products(HS),
counteryname varchar - foreignkey->countries (NAME),
year year,
units int,
weight int,
value int)
- php code was like this
$conn = new mysqli($hn,$un,$pw,$db);
if($conn->connect_error) {die($conn->connect_error);}
$x = 0; // row counter
ini_set('max_execution_time', 3000);
while(!feof($filehandle)){
$x++;
echo $x . ": ";
$fileline = fgets($filehandle);
$fields = explode("\t", $fileline);
$query = "INSERT INTO imports(product_hs,counteryname,year,units,weight,value) VALUES(" . "'" . $fields[0] ."','". $fields[1] . "','2014','". $fields[2] . "','" . $fields[3] . "','" . $fields[4] . "');";
$result = $conn->query($query);
if(!$result) {
echo $conn->error . "</br>";
}else{
echo $result . "</br>";
}
};
first I thought it is an index problem that slows down the insertion , so I removed all the indexes from "imports" table , but it didn't go faster!!
is the problem from the database design or from my php code?
also note that the browser is notifying "waiting for response from the server" for the first 5 minutes then most of the remaining time is notifying "transferring data from server", is this because the response html has more than 40'000 line for the row counter1:1 </br> 2:1 </br> .....(declared in the php code)?
please consider I'm very newbie, thanks.
Thank you very much tadman and Hanlet EscaƱo and Uueerdo and Julie Pelletier and Solarflare
for helping me in the comments.
I did 3 different changes in my PHP code using the approaches you suggested in the comments, then I tested the results and here are the tests results.
The conclusion of the 3 tests: as tadman suggested, the key is in LOAD DATA INFILE . it dramatically reduced the execution time to less than 7 seconds, and these are the 3 tests.
ORIGINAL CODE: ~ 26 minutes
TEST 1 : ~ 34 minutes
(as Uueerdo suggested I removed the echo statements and the rows counter from the loop)
while(!feof($filehandle)){
// $x++; // commented out
//echo $x . ": "; // commented out
$fileline = fgets($filehandle);
$fields = explode("\t", $fileline);
$query = "INSERT INTO products(hs,arabicname,englishname) VALUES(" . "'" . str_replace("'", ".", $fields[0]) ."'," . "'". str_replace("'", ".", $fields[1]) . "'," . "'". str_replace("'", ".", $fields[2]) . "');";
$result = $conn->query($query);
/* // commented out
if(!$result) {echo $conn->error . "</br>";}
}else{echo $result . "</br>";}
*/};
TEST 2 : ~ 7 seconds
(As tadman said I searched for LOAD DATA INFILE and it was super powerful
//replace the entire loop with this simple query
$query = "LOAD DATA LOCAL INFILE'" .
addslashes("C:\\xampp\\htdocs\\bots\\impandexp\\imports.txt")
. "' INTO TABLE imports FIELDS TERMINATED BY '\t' LINES TERMINATED BY
'\r\n'(product_hs,counteryname,units,weight,value) SET year = '2014';";
$result = $conn->query($query);
TEST 3 : ~ 5 seconds
It was the same as test 2 except that I found useful tips on the same page that tadman give, that help in maximizing the speed for.
Bulk Data Loading for InnoDB Tables
// turning off index checks that might slows down bulk data insertion
$query = "SET foreign_key_checks=0;";
$conn->query($query);
$query = "SET unique_checks=0;";
$conn->query($query);
$query ="SET autocommit=0;";
$conn->query($query);
$query = "LOAD DATA LOCAL INFILE'" . addslashes("C:\\xampp\\htdocs\\bots\\impandexp\\imports.txt") . "' INTO TABLE imports FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\r\n'(product_hs,counteryname,units,weight,value) SET year = '2014';";
$result = $conn->query($query);
echo $result . "</br>";
// turning them on again
$query = "SET foreign_key_checks=1;";
$conn->query($query);
$query = "SET unique_checks=1;";
$conn->query($query);
$query ="COMMIT;";
$conn->query($query);
Related
I ran into a problem with strange behavior of mysqli_query. There's a foreach-loop to update table rows but the update for the last loop doens't get written to the database (all the other updates get written successfully). There's no error and mysqli_affected_rows returns 1 as it should. The code pases through till the end, confirmed by an echo-statement after mysqli_query.
I tried to loop trough an array with only one element and the same problem.
What's going on here? Does anyone ran into the same problem?
OK...here's the significant part of the code, the whole code isn't possible cause there maximum 30000 characters allowed. I also echoed out the sql-string and everything is perfect. But as I said the update for the last loop (the last time when the loop is beeing executed) doesn't get written to the MySQL-DB.
$sql_getRelations = "SELECT id_machine, device_id, serial_number, variant_id, company_id, last_file FROM view_machines "
. "WHERE company_id NOT IN ('" . implode( '\',\'' , $btc_companies) . "') "
// . "AND id_machine = 158 "
. "AND device_type_id = 4 AND machine_status = 'active' "
. "ORDER BY id_machine ASC;";
$result_relations = mysqli_query($db_ed, $sql_getRelations);
$relations_arr = mysqli_fetch_all($result_relations, MYSQLI_ASSOC);
mysqli_free_result($result_relations);
foreach($relations_arr as $machine){
//...some other code here
$sql_update_device = "UPDATE device SET last_file = '" . $str_max_ts . "' WHERE id_device = " . $machine['device_id'] . ";";
$update_device = mysqli_query($db_ed, $sql_update_device);
if (!$update_device) {
error_log("\r\n" . date("Y-m-d H:i:s") . ': !!!!Updatefehler device: ' . $machine['device_id'] . ": " . mysqli_error($db_ed), 3, "C:/xampp/htdocs/cronjob/error_log.txt");
}
echo $machine['id_machine'] . ', ';
}
mysqli_close($db_ed);
I would perform debugging like the following to a text file. Wedge your data collection in the outer loops then rem that out and zero in on inner chunks. A divide and conquer strategy.
I would have tried to get my head into your email php but it is 675 lines of one foreach block :p
PHP:
<?php
//mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
mysqli_report(MYSQLI_REPORT_ALL);
error_reporting(E_ALL); // report all PHP errors.
ini_set("display_errors", 1);
echo "start<br/>";
// $file="/home/nate/debug123.txt";
$file="c:\\nate\\debug123.txt";
date_default_timezone_set('America/New_York');
$debugStr=date('Y-m-d H:i:s').": Start";
file_put_contents($file, $debugStr."\n", FILE_APPEND | LOCK_EX);
try {
$mysqli= new mysqli('localhost', 'theUser', 'thePassword', 'theDB_Name');
if ($mysqli->connect_error) {
die('Connect Error (' . $mysqli->connect_errno . ') '
. $mysqli->connect_error);
}
echo "I am connected and feel happy.<br/><br/>";
$query = "INSERT INTO `t921`(`thing`,`uniqueNum`,`views`) values ('aaa',577,0)"; // change that 577 each time else it fails unique
echo $query."<br>";
$mysqli->query($query);
$debugStr=date('Y-m-d H:i:s').": Affected rows: " .$mysqli->affected_rows.", Insert id: ".$mysqli->insert_id;
echo $debugStr."<br><br>";
file_put_contents($file, $debugStr."\n", FILE_APPEND | LOCK_EX);
$query = "UPDATE `t921` SET `views`=`views`+1 WHERE id=1";
echo $query."<br>";
$mysqli->query($query);
$id=1;
$debugStr=date('Y-m-d H:i:s').": UPDATE id=".$id.", Affected rows: " . $mysqli->affected_rows;
echo $debugStr."<br><br>";
file_put_contents($file, $debugStr."\n", FILE_APPEND | LOCK_EX);
$query = "UPDATE `t921` SET `views`=`views` WHERE id=1";
echo $query."<br>";
$mysqli->query($query);
$id=1;
$debugStr=date('Y-m-d H:i:s').": UPDATE id=".$id.", Affected rows: " . $mysqli->affected_rows;
echo $debugStr."<br><br>";
file_put_contents($file, $debugStr."\n", FILE_APPEND | LOCK_EX);
$mysqli->close();
} catch (mysqli_sql_exception $e) {
throw $e;
}
?>
Sample log file (debug123.txt):
2016-09-19 18:23:57: Start
2016-09-19 18:23:57: Affected rows: 1, Insert id: 27
2016-09-19 18:23:57: UPDATE id=1, Affected rows: 1
2016-09-19 18:23:57: UPDATE id=1, Affected rows: 0
Note the last update stmt was meant to change no data thus affected=0
Schema:
create table t921
( id int auto_increment primary key,
thing varchar(100) not null,
uniqueNum int not null,
views int not null,
unique key(uniqueNum)
);
I have a PHP script that is passing large quantities of queries to a DB very quickly. Does a MySQL DB queue the queries as they come in if it can't process them at the same speed they are being passed, or do they get lost?
My program has written and passed syntactically correct queries to the DB, but the DB is very far behind in terms of information contained in tables and number of tables.
Some example code (I am slightly new to PHP, so my code/coding style may be horrifying):
//If table has one primary key
$val = $tblColPkArray[0];
$pkInsert = ", PRIMARY KEY (". $val['COLUMN_NAME'] .")";
$pkColName = $val['COLUMN_NAME'];
$string = ltrim($string, ",");
$oneCreateTableQuery = $beginning . $string . $pkInsert . $end;
echo $oneCreateTableQuery . "\n";
$newLink->query($oneCreateTableQuery);
$pkValuesInOld = "SELECT " . $pkColName . " FROM " . $tables . ";";
$pkValsResult = $link->query($pkValuesInOld);
while($pkValues = $pkValsResult->fetch(PDO::FETCH_ASSOC))
{
$pkRowValuesQuery = "SELECT * FROM " . $tables . " WHERE " . $pkColName . " = '" . $pkValues[$pkColName] . "';";
echo $pkRowValuesQuery . "\n";
$valsOfPkInOldTable = $link->query($pkRowValuesQuery);
while($pkVals = $valsOfPkInOldTable->fetch(PDO::FETCH_ASSOC))
{
//var_dump($ckVals);
$insertBeginning = "INSERT INTO " . $tables . "(";
$insertValuesSection = ") VALUES (";
$insertEnd = ");";
$keys = "";
$rowValues = "";
foreach($pkVals as $key => $value)
{
$keys = $keys . ", " . $key;
$rowValues = $rowValues . ", '" . $value . "'";
}
$insertStatement = $insertBeginning . ltrim($keys, ",") . $insertValuesSection . ltrim($rowValues, ",") . $insertEnd;
echo $insertStatement . "\n";
$newLink->query($insertStatement);
}//While loop: Inserting values of old table into new table via INSERT INTO statement
//unset();
} //While loop: Selecting all values from old table based on PK values per table, pass result of that query to next while loop
You can do this in a single query, instead of calling multiple insert statements.
For instance, instead of running these 3 queries,
INSERT INTO table VALUES(1, 2);
INSERT INTO table VALUES(3, 4);
INSERT INTO table VALUES(5, 6);
...
You could run this query:
INSERT INTO table VALUES(1, 2), (3, 4), (5, 6), ...;
Looks like you could do even combine the INSERT and SELECT:
INSERT INTO table (...)
SELECT ... FROM ...;
Furthermore, your nested loops look like this might work:
INSERT INTO table (...)
SELECT ... FROM ...
JOIN ...;
That would get it down to one call to ->query() and eliminate most of your code.
Below is my small code for inserting some info into AthleteID. It doesn't actually insert the information to the table though, any help is appreciated. (sorry for asking twice, but I think my first question isn't addressing whatever issue is holding me up here!)
<?php
require_once('resources/connection.php');
echo 'hello noob' . '<br />';
$query = mysql_query('SELECT LName, MyWebSiteUserID FROM tuser WHERE MyWebSiteUserID = MyWebSiteUserID');
$athleteId = strtoupper(substr($row["LName"], 0, 2)) . $row["MyWebSiteUserID"];
$update = "UPDATE `tuser` SET `AthleteID`='$athleteId' WHERE `MyWebSiteUserID` = `MyWebSiteUserID`;";
while($row = mysql_fetch_array($query)){
mysql_query( $update);
}
Where to begin..
1) Your using mysql and not mysqli. mysql is now deprecated but you could be on a PHP 4 system so keep that in mind.
2) You are building the $athleteID before you have found out what LName and SkillshowUserID is.
3) Your using a where of 1 = 1. You dont need this as it will return true for every row.
4) So...
// Execute a query
$results = mysql_query('SELECT LName, MyWebsiteID FROM tuser WHERE SkillshowUserID = SkillshowUserID');
// Loop through the result set
while($row = mysql_fetch_array($query))
{
// Generate the athleteId
$athleteId = strtoupper(substr($row["LName"], 0, 2)) . $row["MyWebsiteID"];
// Generate an sql update statement
$update = "UPDATE `tuser` SET `AthleteID`='" . $athleteId . "' " .
" WHERE LName = '" . $row['LName'] . "' " .
" AND MyWebsiteID = '" . $row['MyWebsiteID'] . "';";
// Fire off that bad boy
mysql_query($update);
}
My part_no column has the following format: 000-00000-00 for all records.
I need to extract the five middle characters from part_no and place it in the core column when I create the record.
I can't get my script to work.
I'm not getting any errors. Just not working.
$order = "INSERT INTO cartons_added (add_time, type, part_no, add_type, add_qty, add_ref, add_by, add_notes)
VALUES
('$date',
'$_POST[type]',
'$_POST[part_no]',
'$_POST[add_type]',
'$_POST[add_qty]',
'$_POST[add_ref]',
'$_POST[add_by]',
'$_POST[add_notes]')";
$result = mysql_query($order);
$query2 = "select part_no from cartons_current";
$sel = mysql_query($query2);
$res = mysql_result($sel);
while($row = mysql_fetch_row($res)) {
$core_digits = split('-',$row[0]);
$core =$core_digits[1];
$query3 = "insert into cartons_current(core) values($core)";
$sel2 = mysql_query($query3);
}
You can update your cartons_current table based on your cartons_added table with something like:
INSERT INTO cartons_current(core)
SELECT SUBSTR(part_no, 5, 5) FROM cartons_added
You will probably want to limit that with a WHERE clause or maybe deal with what happens when this value is already in cartons_current (use either INSERT IGNORE or ON DUPLICATE KEY UPDATE)
You are right, the script has no error.
I think the problem is on your SQL that made you can't insert a new row, specifically on the table structure. Maybe you defined a PRIMARY KEY without AUTO_INCREMENT, defined a INDEX or UNIQUE key that is not the core key or there have some other key that did not have default value. Remember that you can't insert a row without defining all required field.
You script is selecting all part_no and for every part_no you are inserting a new row in the same table, so maybe there is the problem.
I think what you want is update every result to add they core value, you can do that with UPDATE as this code:
function getValue($value) {
return "'" . trim(mysql_real_escape_string($value)) . "'";
}
mysql_query('INSERT INTO `cartons_added` (`add_time`, `type`, `part_no`, `add_type`, `add_qty`, `add_ref`, `add_by`, `add_notes`)
VALUES (' .
getValue($date) . ', ' .
getValue($_POST[type]) . ', ' .
getValue($_POST[part_no]) . ', ' .
getValue($_POST[add_type]) . ', ' .
getValue($_POST[add_qty]) . ', ' .
getValue($_POST[add_ref]) . ', ' .
getValue($_POST[add_by]) . ', ' .
getValue($_POST[add_notes]) .
')');
$partNoQuery = mysql_query('SELECT `part_no` FROM `cartons_current`');
while($partNoResult = mysql_fetch_assoc($partNoQuery)) {
list($prefix, $core, $suffix) = explode('-', $partNoResult['part_no']);
mysql_query('UPDATE cartons_current SET `core` = \'' . $core . '\' WHERE `part_no` = \'' . $partNoResult['part_no'] . '\'');
}
I added getValue function to escape posted data to prevent SQL injection.
Try removing this
$res = mysql_result($sel);
And change your while to reference the main query resource
while($row = mysql_fetch_row($sel)) {
I don't understand your logic with your tables though. You're inserting data into the cartons_added table but then you're selecting from cartons_current?
Also, split is deprecated as of PHP 5.3.0
You said five middle "characters", so I'd add quotes around your variable like so:
$query3 = "insert into cartons_current(core) values('$core')";
(Also, there's only about a gazillion answers on SO about SQL injection, and using pdo)
INSERT INTO cartons_current(core)
SELECT
substr(part_no,position('-' IN part_no)+1,position('-' IN substr(part_no,position('-' IN part_no)+1))-1)
FROM cartons_added;
I am fetching data from some tables & storing it in a variable like below-:
$result = mysql_query("SELECT * FROM sample where column=mysql_insert_id()");
while($row=mysql_fetch_array($result))
{
$str = "'". $row["name"] . "',". "'" . $row[quantity] . "'," . "'" . $row["id"];
}
So in my variable $str, suppose I have following values-:
shirt,10,1,pant,50,2....i.e. it will store values in a comma separated format.
Now I want to insert these values in another table say test-:
$qry = "INSERT INTO test(name,quantity,id)values(".$str.");
Now I want to store values in test table in two rows like-:
shirt 10 1
pant 50 2
So how to do the same for Mysql & Oracle tables?
Plz help
See my below query-:
$query2 = "SELECT sfoi.name, sfoi.sku, sfoi.qty_ordered, sfoi.price, sfoi.row_total, sfo.base_subtotal, sfo.base_shipping_amount, sfo.base_grand_total,
(select mso.order_primary from mysql_sales_order mso where mso.increment_id =sfo.increment_id)
FROM sales_flat_order sfo
JOIN sales_flat_order_item sfoi
ON sfoi.order_id = sfo.entity_id
WHERE sfo.increment_id = ". $order_id ;
$result_query2 = mysql_query($query2);
So for one order id i.e. for one order may contain more than 1 products i.e. many name,sku,quantity ordered etc. So at the time of mysql_fetch_array(), I want all product data in a single variable...my code for fetching is like this-:
while($row = mysql_fetch_array($result_query2))
{
$string = "'". $row["name"] . "',". "'" . $row["sku"] . "'," . "'" . $row["qty_ordered"] . "',". "'" . $row["price"] . "'," . "'" . $row["row_total"] . "'," . "'" . $row["base_subtotal"]. "'," . "'" . $row["base_shipping_amount"] . "'," . "'" . $row["base_grand_total"] ."'," . $row["prod_foreign"];
$query3 = "INSERT into mysql_sales_product(name, sku, qty_ordered, price, row_total, base_subtotal,base_shipping_amount,base_grand_total,prod_foreign) VALUES(".$string.")";
}
$result_query_product_outbound = mysql_query($query3);
Here I want to store result of mysql_ fetch_array in variable in such a way that if there are multiple rows I can still able to pass those rows using variable$string. e.g-:
name sku qty_ordered price row_total subtotal shipping_amnt grand_total prod_foreign
nokia nk 2 500 1000 1000 300 1300 11
sansung sam 3 400 1200 1200 500 1700 30
sony sny 4 200 800 800 200 1000 45
For Oracle, like below :
INSERT ALL
INTO your_table(column1,column2,column3) VALUES('values1.1', 'values1.2', 'values1.3')
INTO your_table(column1,column2,column3) VALUES('values2.1', 'values2.2', 'values2.3')
SELECT * FROM dual;
Easier than doing a PHP loop, you can do this directly in MySQL. I'm not sure this is an option for you, since you didn't told us what the matter with Oracle...
Anyway, here is what I would do in your case:
INSERT INTO test(name,quantity,id)
SELECT name, quantity, id FROM sample where id=<your id here>
Or, if you have multiple ids,
INSERT INTO test(name,quantity,id)
SELECT name, quantity, id FROM sample where id in (<your ids here as comma separated list>)