Deleting records inside a foreach loop - php

I have an array that I am passing through a $_POST variable. In the foreach loop, I would like to check to see if "delete" has been checked. If so, I will delete the record. If delete is not checked, I will update the record. When I run this the update works perfect. If I have delete checked - it will delete the first record in the array - not the record with the corresponding id of the record with the checked box. If I have 2 checked - it will take the first 2. If I have them all checked - it will obviously then delete all the records. I have been wrestling with this one all day - looking for a little help. Thanks.
if (isset($_POST['update'])) {
$slo_id = $_POST['slo_id'];
foreach ($_POST['score_id'] as $key=>$score_id) {
$del = $_POST['del'][$key];
if ($del == 'checked') {
$sql2 = " DELETE FROM slo_score WHERE score_id = $score_id ";
#execute SQL statement
$result = mysql_query($sql2);
# check for error
if (mysql_error()) { print "Database ERROR in SQL Update Statement: " . mysql_error(); }
}
else {
$growth_target = $_POST['growth_target'][$key];
$final_score = $_POST['final_score'][$key];
$meets = $_POST['meets'][$key];
//update table
$sql1 = "UPDATE slo_score SET growth_target='$growth_target',final_score='$final_score',meets='$meets' WHERE score_id = $score_id";
#execute SQL statement
$result = mysql_query($sql1);
# check for error
if (mysql_error()) { print "Database ERROR in SQL Update Statement: " . mysql_error(); }
}
}
header("location:...);
}

try this
if (isset($_POST['update'])) {
$slo_id = $_POST['slo_id'];
foreach ($_POST['score_id'] as $key=>$score_id) {
if (isset($_POST['del'][$key])) {
//use mysqli to delete the record
}
else {
$growth_target = $_POST['growth_target'][$key];
$final_score = $_POST['final_score'][$key];
$meets = $_POST['meets'][$key];
//update table
use mysqli to update the record
}
}
header("location:...);
}

You should avoid looping deletes to MySQL at all costs.
If you are receiving an array of IDs to delete, why not just send that in one (or a few, if this is thousands of IDs we're talking about; chunk them) query:
$score_ids = isset($_POST['score_id']) ? $_POST['score_id'] : array();
$score_ids_in = implode("', '", $score_ids);
$sql = sprintf("DELETE FROM slo_score WHERE score_id IN ('%s')", $score_ids_in);
$result = mysql_query($sql);
Of course, this won't give you line by line feedback but it will keep your database from being choked to death.
Also, you already know you should use Mysqli to keep people from destroying your database, extracting sensitive data from it, or both. It's not a huge change from what you're doing now, except it's a lot more secure.

Related

Updating a 4th table using 3 different tables

I've got 3 different tables and I want to update the 4th table with some specific column from each of the 3 tables, they all have a common key. I can do this from the phpmyadmin, but I want to do it using a php script.
This is what I tried but it didn't work
if (isset($_GET)) {
$update = '';
$count="SELECT * FROM test2 ";
foreach ($connect->query($count) as $row) {
$term_total1=$row['english'];
$sql = "UPDATE total set `f_test` ='$term_total1' ";
foreach ($connect->query($count) as $row) {
echo "success<br>" . $term_total1;
}
}
}else{
echo "try another method" . mysqli_error($connect);
}
Have been trying for days now.
Repeated the same code for the other two tables but it won't work.
Is it possible to do it in a single query? If Yes, then how
I'm pretty sure your method of using foreach to loop the result set is incorrect. In your updated code you've also not got a unique identifier so your code is just going to mass update your table. Here's your current code fixed up so hopefully you can understand how to loop the dataset from mysqli
$count="SELECT * FROM test2";
if($res = $connect->query($count)){
while($row = $res->fetch_assoc()){
$term_total1 = $row['english'];
$sql = "UPDATE total set `f_test` = '{$term_total1}'";
if($res2 =$connect->query($sql)){
echo "success<br>" . $term_total1;
}else{
print_r($connect->error());
}
}
}else{
print_r($connect->error());
}

checking if column is zero before updating

How do i insert into a database checking if the column is zero before inserting into it
this is what i have tried but after updating it still insert a new row with the last inserted ID but rather i just want it to update without inserting
$lastID = mysqli_insert_id($DBcon);
$query2=$DBcon->query("SELECT * FROM mergeing");
$compare_value = "0";
if($row = $query2->fetch_array()) {
$merger = "INSERT into mergeing(donator_1) VALUES ('$lastID')";
if($row['donator_2'] !== "$compare_value") {
if ($DBcon->query($merger)) {
echo "success second";
}
}
}
while ($row = $query2->fetch_array()) {
$idd= $row["_id"];
$merg2 = "UPDATE mergeing SET donator_2='".$lastID."' WHERE _id=$idd";
if($row['donator_2'] === "$compare_value") {
if ($DBcon->query($merg2)) {
echo "success";
}
}
}
It seems you want to check if there is a record that has a specific donator_2 value, and if so, you want it updated. If no such value is present, you want to insert a new record.
Then your code could look like this:
$lastID = mysqli_insert_id($DBcon);
$compare_value = 0;
$DBcon->query("UPDATE mergeing SET donator_2 = $lastID WHERE donator_2 = $compare_value");
if ($DBcon->affected_rows) {
echo "success: updated";
} else {
$DBcon->query("INSERT into mergeing(donator_1) VALUES ($lastID)");
echo "success: inserted";
}
Depending on your actual use case, but you might want to also set the donator_2 value when you insert the new record.
Please note that if the $compare_value is determined by user input or some other source that you cannot predict, then you should use prepared statements, as otherwise the code is open to SQL injection.

Insert into MySQL db occurs twice in a loop

I'm currently building a small site to retrieve player stats for a computer game from a mysql db and display them online.
I get player statistics for a list of players from a third party API and am trying to insert them into a table in the db. However my code is performing the inserts twice - the primary key restriction on the table stops duplicate entries, but I don't want to accept this.
Something is amiss with my looping logic, but I'm going in my own loop trying to figure this one out.
Sequence of events is:
I query my db to get the player ID's needed for the API calls
I put these in an array
I query the third party api in a loop to get all the player stats
I want to insert the stats (1 row per player) to my db (I plan to escape the strings etc, it's a work in progress)
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$member_data[] = $row;
}
foreach ($member_data as $id) {
$endpoint = "http://www.bungie.net/Platform/Destiny/Stats/2/".$id[destiny_membership_id]."/".$id[destiny_character_id]."/";
$bungie_result = hitBungie($endpoint);
$response = json_decode($bungie_result, true);
$destiny_membership_id = $id[destiny_membership_id];
$destiny_character_id = $id[destiny_character_id];
$kills_deaths_ratio = $response[Response][allPvP][allTime][killsDeathsRatio][basic][displayValue];
// DB insert
$sql = "INSERT INTO xax_pvp_stats (destiny_membership_id,destiny_character_id,kills_deaths_ratio) ";
$sql .= "VALUES ('$destiny_membership_id','$destiny_character_id','$kills_deaths_ratio') ";
$result = $conn->query($sql);
if ($conn->query($sql) === FALSE) {
echo "<br />Error: " . $sql . "<br />" . $conn->error;
} else {
echo $destiny_character_id." is in the DB";
}
}
} else {
echo "0 results";
}
You're executing the query twice. Once here:
$result = $conn->query($sql);
and once here:
if ($conn->query($sql) === FALSE) {
I'm guessing you meant to examine the result in the second line:
if ($result === FALSE) {
Several issues, starting with the one you're worried about (insert happening twice):
1) You're calling $conn->query twice, which, of course, executes the INSERT query twice:
Here:
$result = $conn->query($sql);//insert1
if ($conn->query($sql) === FALSE) {//insert 2
echo "<br />Error: " . $sql . "<br />" . $conn->error;
} else {
echo $destiny_character_id." is in the DB";
}
2) Your code is vulnerable to injection, learn about prepared statements and use them
3) accessing values in an associative array requires the keys to be quoted: $response[Response][allPvP][allTime][killsDeathsRatio][basic][displayValue] issues notices. When developing, always use display_errors, and set the error level as strict as you can E_STRICT|E_ALL is recommended.
You're running the query twice: once when you set $result then again in the following if statement. Remove the $result line (you're not using that variable anyway) and you'll be good to go.

PHP + mySQL when inserting many rows in loop to table my php hungs/freases/

I have some strange problem with inserting rows in loop into mySQL table.
Let me show you php code first that I use then I describe some statistic.
I tend to think that it is some mySQL issue, but absolutely no idea what kind of. Max inserts in table per minute? (Can't be max row reached - planty of spase on disk)
echo " For character=" . $row[1];
$xml = simplexml_load_file($api_url);
$i=0;
foreach ($xml->result->rowset->row as $value) {
$newQuery = 'INSERT INTO '.$tableName.' (transactionDateTime, quantity, typeName, price, clientName, station, transactionType, seller) VALUES ("'.$value['transactionDateTime'].'",'.$value['quantity'].',"'.$value['typeName'].'","'.$value['price'].'","'.$value['clientName'].'","'.$value['stationName'].'","'.$value['transactionType'].'","'.$row[1].'")';
$i++;
if (!mysqli_query($conn, $newQuery)) {
die('Error while adding transaction record: ' . mysqli_error($conn));
} // if END
} // foreach END
echo " added records=" . $i;
I have same data in XML that doesn't change. (XML has something like 1400+ rows that i would insert)
It always inserts different amount of rows. Max amount it inserted was around 800+
If I insert like 10sec delay into foreach loop at $i==400 it will add even less rows. And more delays - less rows.
It never comes to that part of code where mysqli_error($conn)
It never reaches echo " added records=" . $i; part of the code.
Since it alwasy stops on different recors I have to assume nothing wrong with INSERT query.
Since it never reaches line after foreach loop echo " added records=" . $i; I also assume XML data wasn't processed by the end of it.
If I use another sources of data (another character) where are less records in XML then this code works just fine.
What could possibly be my problem?
Could be that your firing multiple queries at your SQL server. Better to build a single SQL query via your foreach then fire it once.
Something like this, basically:
$db = new mysqli($hostname, $username, $password, $database);
if($db->connect_errno > 0)
{
$error[] = "Couldn't establish connection to the database.";
}
$commaIncrement = 1;
$commaCount = count($result);
$SQL[] = "INSERT INTO $table $columns VALUES";
foreach ($result as $value)
{
$comma = $commaCount == $commaIncrement ? "" : ",";
$SQL[] = "(";
$SQL[] = "'$value[0]'"."'$value[1]'"."'$value[2]'"."'$value[3]'";
$SQL[] = ")$comma";
$commaIncrement++;
}
$SQL[] = ";";
$completedSQL = implode(' ',$SQL);
$query = $db->prepare($completedSQL);
if($query)
{
$db->query($completedSQL)
}
$db->close();
Scrowler is right, your php is timing out. As a test, you can add
set_time_limit(0);
to the start of your php script.
WARNING - Don't use this in production or anywhere else. Always set a reasonable time limit for the script.

How to identify the query that caused the error using mysqli_multi_query?

Using a example from elsewhere on SO to better catch 'hiding' errors. While the code below will catch and return an error, is it possible to improve this to report for which query the error occurred?
With the code below, the output is:
Columns: 18
Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FRO inventory' at line 1
Code being tested:
$query = "SELECT * FROM orders WHERE location = 'IN' ORDER BY orderNum DESC LIMIT 20;";
$query .= "SELECT * FRO inventory"; // With error
$ord = array();
$invent = array();
if(mysqli_multi_query($link, $query)) {
do {
// fetch results
if($result = mysqli_store_result($link)) {
echo 'Columns: ' . mysqli_field_count($link) . "<br>";
while($row = mysqli_fetch_assoc($result)) {
if(count($row) > 17)
$orders[] = $row;
elseif(count($row) == 6)
$inv[] = $row;
}
}
if(!mysqli_more_results($link))
break;
if(!mysqli_next_result($link)) {
// report error
echo 'Error: ' . mysqli_error($link);
break;
}
} while(true);
mysqli_free_result($result);
}
Here is an approach that will not only improve the quality of your error messages, it will improve the way you handle your result sets.
$q["Orders"] = "SELECT * FROM orders WHERE location = 'IN' ORDER BY orderNum DESC LIMIT 20";
$q["Inventory"] = "SELECT * FRO inventory";
if (!$link = mysqli_connect("host", "user", "pass", "db")) {
echo "Failed to connect to MySQL: " , mysqli_connect_error();
} elseif (mysqli_multi_query($link, implode(';', $q))) {
do {
$q_key = key($q); // current query's key name (Orders or Inventory)
if ($result = mysqli_store_result($link)) { // if a result set... SELECTs do
while ($row = mysqli_fetch_assoc($result)) { // if one or more rows, iterate all
$rows[$q_key][] = $row;
}
mysqli_free_result($result);
echo "<div><pre>" . var_export($rows[$q_key], true) . "</pre></div>";
}
} while (next($q) && mysqli_more_results($link) && mysqli_next_result($link));
}
if ($mysqli_error = mysqli_error($link)) { // check & declare variable in same step to avoid duplicate func call
echo "<div style=\"color:red;\">Query Key = " , key($q) , ", Query = " , current($q) , ", Syntax Error = $mysqli_error</div>";
}
Error on first query:
If your first query tries to access a table that doesn't exist in the nominated database like: ordersXYZ
Array $rows will not exist, no var_export() will occur, and you will see this response:
Query Key = Orders, Query = SELECT * FROM ordersXYZ WHERE location='IN' ORDER BY orderNum DESC LIMIT 20, Syntax Error = Table '[someDB].ordersXYZ' doesn't exist
Error on second query:
If your first query is successful, but your second query tries to access a non-existent table like: inventory2
$rows["Orders"] will hold the desired row data and will be var_export()'ed, $row["Inventory"] will not exist, and you will see this response:
Query Key = Inventory, Query = SELECT * FROM inventory2, Syntax Error = Table '[someDB].inventory2' doesn't exist
No errors:
If both queries are error free, your $rows array will be filled with the desired data and var_export()'ed, and there will be no error response. With the queried data saved in $rows, you can access what you want from $rows["Orders"] and $rows["Inventory"].
Things to note:
You may notice that I am making variable declarations and conditional checks at the same time, this makes the code more concise but some devs prefer to avoid this.
As my approach uses implode() with a semi-colon on the elseif line, be sure not to add a trailing semi-colon to your queries.
This set of queries always returns a result set because all are SELECT queries, if you have a mixed collection of queries that affect_rows, you may find some useful information at this link(https://stackoverflow.com/a/22469722/2943403).
mysqli_multi_query() will stop running queries as soon as there is an error. If you are expecting to catch "all" errors, you will discover that there will never be more than one.
Writing conditional break points like in the OP's question and solution is not advisable. While custom break points may be rightly used in other circumstances, for this case the break points should be positioned inside of the while() statement of the do() block.
A query that returns zero rows will not cause a error message -- it just won't create any subarrays in $rows because the while() loop will not be entered.
By using the key() function, the OP's if/elseif condition that counts the columns in each resultset row can be avoided. This is better practice because running a condition on every iteration can become expensive in some cases. Notice that the array pointer is advanced inside of $q at the end of each do() iteration. This is an additional technique that you will not find on the php manual page; it allows key() to work as intended.
And, of course, the <div><pre>var_export()...</pre></div> line can be removed from your working code -- it was purely for demonstration.
If you are going to run any more queries after this code block that reuse variables, be sure to clear all used variables so that residual data does not interfere. e.g. $mysqli_error=null; // clear errors & reset($q); // reset array pointer.
Take heed to this somewhat vague warning at your own discretion: http://php.net/manual/en/mysqli.use-result.php :
One should not use mysqli_use_result() if a lot of processing on the
client side is performed, since this will tie up the server and
prevent other threads from updating any tables from which the data is
being fetched.
Lastly and MOST IMPORTANTLY for security reasons, do not display query or query error information publicly -- you don't want sinister people to see this kind of feedback. Equally important, always protect your queries from injection hacks. If your queries include user-provided data, you need to filter/sanitize the data to death before using it in mysqli_multi_query(). In fact when dealing with user input, my very strong recommendation is to move away from mysqli_multi_query() and use either mysqli or pdo prepared statements for your database interactions for a higher level of security.
To answer my own question and since the documentation is poor, here's a solution that hopefully will help others. What was missing is a way to catch an error on the 1st query. (The hidden actions of myqsqli_multi_query are difficult to understand.)
Now check for entries in $err array.
$q[1] = "SELECT * FROM orders WHERE location = 'IN' ORDER BY orderNum DESC LIMIT 20";
$q[2] = "SELECT * FROM inventory";
$ord = array();
$invent = array();
$err = array();
$c = 1;
if(mysqli_multi_query($link, implode(';', $q))) {
do {
// fetch results
if($result = mysqli_use_result($link))
while($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
if(count($row) > 17)
$orders[] = $row;
elseif(count($row) == 6)
$inv[] = $row;
}
}
$c++;
if(!mysqli_more_results($link))
break;
if(!mysqli_next_result($link) || mysqli_errno($link)) {
// report error
$err[$c] = mysqli_error($link);
break;
}
} while(true);
mysqli_free_result($result);
}
else
$err[$c] = mysqli_error($link);
mysqli_close($link);
In your do loop, add a counter, each successful mysqli_next_result increment the counter. Once mysqli_next_result returns false, output the counter as well.
This works for two queries.
If the error is in the first, the response for 1st query to PHP is the error message, and for the second (which won't fire), a message tattling on the first.
If the error is in the second, the first's response is returned and the 2nd gets the error message.
I'm using associative arrays and nixing array elements[0].
This adds ['Error'] key only if there is a relevant error.
Finally, I'm not the best PHPer, so it's up to you to fix what's ugly.
$query_nbr=0;
if (mysqli_multi_query($link,$query ))
{
do
{
unset($field_info) ; $field_info = array() ;
unset($field_names) ; $field_names = array() ;
unset($values) ; $values = array(array()) ;
if ($result = mysqli_store_result($link))
{
$query_nbr += 1 ;
$rows_found = mysqli_num_rows($result);
$fields_returned = mysqli_num_fields($result);
$field_info = mysqli_fetch_fields($result);
$field_nbr=0;
foreach ($field_info as $fieldstuff)
{ $field_nbr +=1 ;
$field_names[$field_nbr] = $fieldstuff->name ;
}
$now = date("D M j G:i:s T Y") ;
if ($query_nbr == 1)
{
$result_vector1 = array('when'=>$now) ;
$result_vector1['nrows']=0;
$result_vector1['nrows']=$rows_found ;
$result_vector1['nfields']=$fields_returned ;
$result_vector1['field_names']=$field_names ;
}
else
{
$result_vector2 = array('when2'=>$now) ;
$result_vector2['nrows2']=0;
$result_vector2['nrows2']=$rows_found ;
$result_vector2['nfields2']=$fields_returned ;
$result_vector2['field_names2']=$field_names ;
}
$row_nbr=0 ;
while ($row = mysqli_fetch_array($result, MYSQLI_BOTH))
{
$row_nbr++ ;
for ($field_nbr=1;$field_nbr<=$fields_returned;$field_nbr++)
{
$values[$row_nbr][$field_names[$field_nbr]] =$row[$field_nbr-1] ;
}
}
mysqli_free_result($result) ;
unset($values[0]) ;
if ($query_nbr == 1)
{$result_vector1['values']=$values ;}
else
{$result_vector2['values2']=$values ;}
} // EO if ($result = mysqli_store_result($link))
} while (mysqli_more_results($link) && mysqli_next_result($link)) ;
} // EO if (mysqli_multi_query($link,$query ))
else
{
// This will be true if the 1st query failed
if ($query_nbr == 0)
{
$result_vector1['Error'] = "MySQL Error #: ".mysqli_errno($link).": ".mysqli_error($link) ;
$result_vector2['Error'] = "MySQL Error in first query." ;
}
} // EO MySQL
// Here we only made it through once, on the 2nd query
if ( $query_nbr == 1 && $nqueries == 2 && empty( $result_vector2 ) )
{
$result_vector2['Error'] = "MySQL Error #: ".mysqli_errno($link).": ".mysqli_error($link) ;
}

Categories