How to insert or update multiple rows - PDO - php

I have a small problem with the request to the base of the PDO.
I want to insert or update fields in one request. I want insert a few records to DB, but if there are already 'file_name' fields with the selected name then just to update the other fields. Below paste my php code.
For example: I adding 5 records id_product (eg. 1) and various other fields.
Example fields recived from the $_post:
(id_product, alt, file_name, main_photo)
1, xxx, 1_1, 0;
1, xxx, 1_2, 0;
1, xxx, 1_3, 1;
my PHP code:
$query = "INSERT INTO product_image (id_product, alt, file_name, main_photo) VALUES ";
$part = array_fill(0, count($_POST['file_name']), "(?, ?, ?, ?)");
$query .= implode(",", $part);
$query .= " ON DUPLICATE KEY UPDATE alt=VALUES(alt), main_photo=VALUES(main_photo)";
$stmt = $this->_db->prepare($query);
$j = 1;
$im_null = 0;
for ($i = 0; $i < count($_POST['file_name']); $i++) {
$stmt->bindParam($j++, $_POST['id_product'], \PDO::PARAM_INT);
$stmt->bindParam($j++, $_POST['alt'][$i], \PDO::PARAM_STR);
$stmt->bindParam($j++, $profile->base64_to_jpeg($_POST['file_name'][$i], APP_ROOT . '/uploads/products/' . $_POST['id_product'] . '_' . $_POST['file_nr'][$i]), \PDO::PARAM_STR);
($_POST['main_photo'][$i] == 1) ? $stmt->bindParam($j++, $_POST['main_photo'][$i], \PDO::PARAM_INT) : $stmt->bindParam($j++, $im_null);
}
$stmt->execute();
In this query inserts works good, but doesn't update, which is the second part of the request.

Move $j = 1; inside loop as it is you keep on incrementing $j.
$im_null = 0;
for ($i = 0; $i < count($_POST['file_name']); $i++) {
$j = 1;

Related

how come PDO::bindParam is so slow?

SOLVED: The quoting of values in second test seemed to be the problem.
Instead doing 'VALUES ($id, $t1....)' I needed to do 'VALUES ("$id", "$t1".....), in the second test this was indeed throwing errors. which I couldn't see, only after printing them manually.
Hey there I've been working with bindParam lately and noticed the load time of my page went up dramatically.
So I spend a few hours to diagnose the problem and it seems that bindParam is using A LOT more processing time then the old fashion way (using parameters in query directly)
To make sure the results are valid, I did both tests twice.
Here is my test:
for ($j = 0; $j < 2; $j++) {
unset($t);
$dbh = new PDO('mysql:host=localhost;port=3306', 'root', 'root');
$dbh->query('USE Web_Amicrom_HQ');
$t['start'] = microtime(true);
for ($i = 0; $i < 50; $i++) {
$id = rand(1000000, 9999999);
$t1 = string_random(240);
$t2 = string_random(240);
$t3 = string_random(1000);
$ins = $dbh->prepare('INSERT INTO `test` (id, t1, dt, t2, t3) VALUES(:1, :2, now(), :3, :4)');
$ins->bindParam(':1', $id, PDO::PARAM_INT);
$ins->bindParam(':2', $t1, PDO::PARAM_STR);
$ins->bindParam(':3', $t2, PDO::PARAM_STR);
$ins->bindParam(':4', $t3, PDO::PARAM_STR);
$ins->execute();
}
$t['loop_fact'] = microtime(true);
echo "---- with bindParam ----\n";
$str_result_bench = mini_bench_to($t);
echo $str_result_bench; // string return
echo "\n\n";
}
for ($j = 0; $j < 2; $j++) {
unset($t);
$dbh = new PDO('mysql:host=localhost;port=3306', 'root', 'root');
$dbh->query('USE Web_Amicrom_HQ');
$t['start'] = microtime(true);
for ($i = 0; $i < 50; $i++) {
$id = rand(1000000, 9999999);
$t1 = string_random(240);
$t2 = string_random(240);
$t3 = string_random(1000);
$ins = $dbh->prepare("INSERT INTO `test` (id, t1, dt, t2, t3) VALUES($id, $t1, now(), $t2, $t3)");
$ins->execute();
}
$t['loop_fact'] = microtime(true);
echo "---- with parameter in query ----\n";
$str_result_bench = mini_bench_to($t);
echo $str_result_bench; // string return
echo "\n\n";
}
Results:
---- with bindParam ----
total time : 3136.148ms
---- with bindParam ----
total time : 2645.822ms
---- with parameter in query ----
total time : 41.693ms
---- with parameter in query ----
total time : 52.9752ms
Things I tried.
Change quotes from Double to Single - No difference
bindParam without parameter type (e.g PDO::PARAM_INT) - No difference
Changed all param names (e.g :1 to :id etc.) - No difference
This is a big performance difference, especially for just a few queries.
In line 3 in your code you have the following error host=127 that is why bindParam has too slow benchmarks
$dbh = new PDO('mysql:host=127.0.0.1;port=3306', 'root', 'root');
EDIT
In your example you can avoid connecting to MySQL server 4 times (2 loops of 2 times each) and not close the connection once. Also there is not need to have an extra query to USE database Web_Amicrom_HQ, you can pass that piece of information in the connection.
Full code
$dbh = new PDO('mysql:host=localhost;dbname=Web_Amicrom_HQ', 'root', 'root');
for ($j = 0; $j < 2; $j++) {
$start = microtime(true);
for ($i = 0; $i < 50; $i++) {
$id = rand(1000000, 9999999);
$t1 = string_random(240);
$t2 = string_random(240);
$t3 = string_random(1000);
$ins = $dbh->prepare('INSERT INTO `test` (id, t1, dt, t2, t3) VALUES(:1, :2, now(), :3, :4)');
$ins->bindParam(':1', $id, PDO::PARAM_INT);
$ins->bindParam(':2', $t1, PDO::PARAM_STR);
$ins->bindParam(':3', $t2, PDO::PARAM_STR);
$ins->bindParam(':4', $t3, PDO::PARAM_STR);
$ins->execute();
}
echo "---- with bindParam ----\n";
echo microtime(true) - $start; // time elapsed
echo "\n\n";
}
for ($j = 0; $j < 2; $j++) {
$start = microtime(true);
for ($i = 0; $i < 50; $i++) {
$id = rand(1000000, 9999999);
$t1 = string_random(240);
$t2 = string_random(240);
$t3 = string_random(1000);
$ins = $dbh->prepare("INSERT INTO `test` (id, t1, dt, t2, t3) VALUES($id, $t1, now(), $t2, $t3)");
$ins->execute();
}
echo "---- with bindParam ----\n";
echo microtime(true) - $start; // time elapsed
echo "\n\n";
}
$dbh = null;
Q: Why is PDO::bindParam so slow?
A: PDO::bindParam isn't slow.
The benchmark test demonstrated in the question isn't a valid measure of bindParam performance.
The second test is going to throw errors for nearly every INSERT. That's because the string literals (?) are not enclosed in single quotes. I originally thought those would be interpreted as numeric. (Confusingly, we don't see a specification for the return from string_random(240). We don't see a definition of that function, or even any examples of what that returns.)
If that's returning a string of 240 characters which doesn't represent a valid numeric literal... the benchmark is comparing inserts of rows ~1500 characters (using bindParam) vs. no rows inserted.
My original answer (below) indicates modifications to the two tests so that they perform equivalent functions. This would be more valid comparison of bindParam vs not bindParam.
ORIGINAL ANSWER
For improved performance, call prepare and bindParam functions one time, before entering the loop. (It's not necessary to call prepare on the same SQL statement multiple times.)
$sql = 'INSERT INTO `test` (id, t1, dt, t2, t3) VALUES(:1, :2, NOW(), :3, :4)';
$ins = $dbh->prepare($sql);
$id = 0;
$t1 = 0;
$t2 = 0;
$t3 = 0;
$ins->bindParam(':1', $id, PDO::PARAM_INT);
$ins->bindParam(':2', $t1, PDO::PARAM_STR);
$ins->bindParam(':3', $t2, PDO::PARAM_STR);
$ins->bindParam(':4', $t3, PDO::PARAM_STR);
for ($i = 0; $i < 50; $i++) {
$id = rand(1000000, 9999999);
$t1 = string_random(240);
$t2 = string_random(240);
$t3 = string_random(1000);
$ins->execute();
}
If we were using bindValue instead of bindParam, we would need to do the bindValue immediately before the execute, each time.
For the second test (not using bindParam), the code is vulnerable to SQL Injection, unlike the first test. To make this second test equivalent to the first, we'd need to ensure that the values are properly escaped so they are safe for inclusion in the SQL text.
for ($i = 0; $i < 50; $i++) {
$id = rand(1000000, 9999999);
$t1 = string_random(240);
$t2 = string_random(240);
$t3 = string_random(1000);
$sql = "INSERT INTO `test` (id, t1, dt, t2, t3) VALUES ( "
. $dbh->quote( $id ) . ", "
. $dbh->quote( $t1 ) . ", "
. "NOW(), "
. $dbh->quote( $t2 ) . ", "
. $dbh->quote( $t3 ) . ")";
$ins = $dbh->prepare($sql);
$ins->execute();
}
Note that the quote function does more than just put quotes around values. It also "escapes" single quotes included within the text, and other characters that would be interpreted by MySQL. (In the original second test, we don't see single quotes around the values, so it looks like MySQL would have evaluated those in a numeric context. We'd expect a lot of those rows would have "0" in the t1, t2 and t3 columns.
I'd be interested in seeing the comparison in timing of these two (modified) tests.

Writing to database using foreach

I have Three arrays and i want to write them to database , The issue I face is whenever the values are written to the particular column the rest of the column is left empty.
The
$name_array = array(3) { [0]"Name1" [1]=>"Name2" [2]=> "Name3" }
$roll_array = array(3) { [0]=>"1" [1]=>"2" [2]=>"3" }
$att_array = array(3) { [0]=>"Present" [1]=>"Present" [2]=>"absent" }
I have three columns in DB "NAME" "ROLL" "ATTENDANCE"
I want to store all the array data to the database at the same time.
so it should look like this
NAME ROLL ATTENDANCE
Name1 1 present
Name2 2 present
Name3 3 absent
Here is the code i tried but it just add each values to the column and leaves the other column empty. So the first three rows has only ROLLNO and next three row has only NAME and last three rows has only ATTENDANCE.
$name_values = array();
$roll_values = array();
$att_values = array();
foreach ($name_array as $key => $name_values) {
$name_values = mysqli_real_escape_string($connection,$name_values);
$sql= "INSERT INTO `aclass12` (Name) VALUES ('$name_values')";
mysqli_query($connection,$sql);
}
foreach ($roll_array as $key => $roll_values) {
$roll_values = mysqli_real_escape_string($connection,$roll_values);
$sql= "INSERT INTO `aclass12` (RollNo) VALUES ('$roll_values')";
}
foreach ($att_array as $key => $att_values) {
$att_values = mysqli_real_escape_string($connection,$att_values);
$sql= "INSERT INTO `aclass12` (attendance) VALUES ('$att_values')";
}
I know this is not the right way to do . and whats the way to do this ?
Simply use one array as the master, and the key of that array to access the other 2 arrays data.
Then insert all the data in a single INSERT
Its also a good idea to check that the INSERT actually worked, so I added a little bit of error checking
foreach ($name_array as $key => $value) {
$name = mysqli_real_escape_string($connection,$value);
$roll = mysqli_real_escape_string($connection,$roll_values[$key]);
$att = mysqli_real_escape_string($connection,$att_array[$key]);
$sql = "INSERT INTO `aclass12`
(Name, RollNo, attendance)
VALUES ('$value', '$roll', '$att')";
$res = mysqli_query($connection,$sql);
if ( $res === FALSE ) {
echo mysqli_error();
exit;
}
}
Use only one foreach and access the elements of the arrays there. Like this:
foreach ($name_array as $key => $name_values) {
$name_values = mysqli_real_escape_string($connection,$name_values);
$roll_values = mysqli_real_escape_string($connection,$roll_array[$key]);
$att_values = mysqli_real_escape_string($connection,$att_array[$key]);
$sql= "INSERT INTO `aclass12` (Name, RollNo, attendance) VALUES ('$name_values', '$roll_values', '$att_values')";
mysqli_query($connection,$sql);
}
Also, it's recommended to use prepared statements, because they prevent SQL njection attacks. More information here.
Try it this ways
for($i = 0; $i < count($name_array);$i++) {
$name_values = mysqli_real_escape_string($connection,$name_array[$i]);
$roll_values = mysqli_real_escape_string($connection,$roll_array[$i]);
$att_values = mysqli_real_escape_string($connection,$att_array[$i]);
$sql= "INSERT INTO `aclass12` (Name, RollNo, attendance) VALUES ('$name_values', '$roll_values','$att_values')";
}
Other option is to use multidimensional array with foreach.
foreach($name_array as $n_k=>$name) {
$roll = (isset($roll_array[$n_k])) ? $roll_array[$n_k] : '';
$att = (isset($att_array[$n_k])) ? $att_array[$n_k] : '';
$name = mysqli_real_escape_string($connection,$name);
$roll = mysqli_real_escape_string($connection,$roll);
$att = mysqli_real_escape_string($connection,$att);
$sql= "INSERT INTO `aclass12` (Name, RollNo, attendance) VALUES ('$name','$roll','$att')";
mysqli_query($connection,$sql);
}
I do think it would be best to use since mysql query to inject it and simply concatenate everything before that. That's something like this:
$query = "INSERT INTO tbl_name (col1, col2, col3) VALUES ";
for ($i = 0; $i < count($name_array); $i++) {
$name = mysqli_real_escape_string($conn, $name_array[$i]);
$roll = mysqli_real_escape_string($conn, $roll_array[$i]);
$att = mysqli_real_escape_string($conn, $att_array[$i]);
$query .= "('{$name}', '{$roll}', '{$att}'),";
}
$query = trim($query, ',');
$query = $query . ';';
mysqli_query($connection,$sql);
Add some damage control there (check for errors) and that's it.

How to insert multiple values in a single query

Please somebody help me. In below code the query will execute 3 times , means query execution will depend on number of elements in array.
Please guide me how to run this query with inserting all data at once
$products = array("shirt" , "paint" , "socks");
$price = array("200" , "600" , "50");
$quantity = array("3" , "2" , "2");
$num = 0; while($num <= count($products))
{
$mysqli->query("insert into new_order set
product = '".$products[$num]."' ,
price = '".$price[$num]."' ,
quantity = '".$quantity[$num]."'
");
$num++;
}
It won't throw any error untill you'll be getting same number of values within an array
$counts = count($products);
$query = "insert into new_order (product,price,quantity) values ";
foreach($products as $key => $value){
$query .= "('$value','$price[$key]','$quantity[$key]')";
$query .= (++$key == $counts) ? '' : ',';
}
$mysqli->query($query);
Query looks like:
//insert into new_order (product,price,quantity) values('shirt','200','3'),('paint','600','2'),('socks','50','2')
Iterate over each item in $products to build $sql string:
$sql = "insert into new_order(product, price, quantity) values ";
for($i=0;$i<count($products);$i++){
$sql .= "({$products[$i]}, {$price[$i]}, {$quantity[$i]}),";
}
$sql = substr($sql,0,-1); //cut off the trailing comma
$mysqli->query($sql);
// insert into new_order(product, price, quantity) values (shirt, 200, 3),(paint, 600, 2),(socks, 50, 2)

PDO SQL single query, multiple rows and values

I have this array JSON POST request to a PHP file.
Array
(
[user_id] => 1
[date] => 2014-12-05
[time] => 12:00
[description] => lol
[friends] => "12","9"
[PHPSESSID] => 5ae7c3e6339c528e7804020dd0f0cdbb
)
I try to add the values (12 | 1) and (9 | 1) to a mysql table with a single sql query
Table:
u_id | f_id
1 | 12
1 | 9
What I have so far:
$friendarray = $_POST['Friends'];
foreach( $friends as $friendsarray ) {
$values[] = "(" . $u_id . "," . $friendsarray . ")";
}
$query = "INSERT INTO up2_friends_to_users (u_id , f_id ) VALUES ".implode(',',$values);
$stmt = $db->prepare($query);
$result = $stmt->execute();
As you see this is not working at all. I try to achieve something like this:
$query_params = array(
':u_id' => $_POST['user_id'],
':f_id' => $friendid,
And then would like to send it like this:
$stmt = $db->prepare($query);
$result = $stmt->execute($query_params);
Is it possible to create a single query with multiple rows like this?
Answer thanks to RobP:
$friendsarray = explode(',',$_POST['friends']);
$placeholders = [];
for($i=0, $len=count($friendsarray); $i < $len; $i++) {
$placeholders[i] .= "(:u_id".$i.", :f_id".$i.")"; // entries like "(:u_id0, :f_id0)"
}
$query = "INSERT INTO up2_friends_to_users (u_id , f_id ) VALUES ".implode(",", $placeholders);
$stmt = $db->prepare($query);
for($i=0, $len=count($placeholders); $i < $len; $i++) {
$stmt->bindParam(':u_id'.$i, $_POST['user_id']);
$nextFriend = $friendsarray[$i];
$stmt->bindParam(':f_id'.$i,trim($nextFriend,'"'));
}
$result = $stmt->execute();
Now f_id is always null.
I agree the best strategy is to use a single query as you were trying to do. This will be much faster for long lists, especially if you don't wrap all the individual inserts into a single commit. This should work:
$friendarray = $_POST['Friends'];
$placeholders = [];
$user_id = $_POST[`user_id`];
for($i=0, $len=count($friendarray); $i < $len; $i++) {
$placeholders[$i] = "(:u_id".$i.", :f_id".$i.")"; // entries like "(:u_id0, :f_id0)"
}
$query = "INSERT INTO up2_friends_to_users (u_id , f_id ) VALUES ".implode(",", $placeholders);
$stmt = $db->prepare($query);
for($i=0, $len=count($placeholders); $i < $len; $i++) {
// each binding must use a separate variable, not an array element
$stmt->bindParam(':u_id'.$i, $user_id);
// use your favorite escape function on the value here
$nextFriend = $db->real_escape_string($friendarray[$i]);
$stmt->bindValue(':f_id'.$i, $nextFriend);
}
EDIT: learned something new from Only variables can be passed by reference - php. Can't pass array elements to bindParam as second parameter! Workaround posted above.
Do this:
$query = "INSERT INTO up2_friends_to_users (u_id , f_id ) VALUES (:u_id, :f_id)";
$stmt = $db->prepare($query);
$stmt->bindParam(':u_id', $_POST['user_id'];
$stmt->bindParam(':f_id', $friendid);
foreach ($_POST['Friends'] as $friendid) {
$stmt->execute();
};
bindParam binds to a reference, so every time you execute the query it will use the value of $friendid from the current iteration of the loop.
Maybe, something like this (using question mark parameters)?
$values = array();
foreach ($_POST['Friends'] as $friendid) {
$values[] = $u_id;
$values[] = $friendid;
}
$conn = new \PDO($dsn, $user, $password);
$query = 'INSERT INTO up2_friends_to_users (u_id , f_id ) VALUES '
. trim(str_repeat('(?, ?),', count($values / 2)), ',');
$conn->prepare($query)->execute($values);

How to insert the array value inside for loop in php

Here I am having array value inside the for loop, and insert query outside the for loop.
Need to know how to connect the array value inside the insert query
Here My code is
$start = php2MySqlTime(js2PhpTime($st));
$count= (strtotime($et) - strtotime($st)) /60;
$count1 = $count/30; //echo $count1;
for($i=0;$i<=$count1;$i++){
$start = date("Y-m-d H:i:s",strtotime("+30 minutes",strtotime($start))).',';
echo $start;
}
$sql = "insert into `jqcalendar` (`list_id`,`totaltime`, `isalldayevent`)
values ('"
.$list_id."', '".$start."', '".mysql_real_escape_string($ade)."'
)";
By this code it's inserting only one value in array, But I need full array values to be inserted
Try this..Its just example to show you logic
$qry = 'INSERT INTO table (FirstName, LastName) VALUES ';
for($i=0;$i<=$count1;$i++){
$qry .= "($value['firstname'],$value['lastname']), ";
}
for ($i =0; $i< count($date); $i++ )
{
$data = array(
'date' => $date[$i]
);
$rs =$this->db->insert('table_name', $data);
}
Use :
$start = "";
for($i=0;$i<=$count1;$i++){
$start .= date("Y-m-d H:i:s",strtotime("+30 minutes",strtotime($start))).',';
echo $start;
}
$sql = "insert into `jqcalendar` (`list_id`,`totaltime`, `isalldayevent`)
values ('"
.$list_id."', '".$start."', '".mysql_real_escape_string($ade)."'
)";

Categories