I have two while loops running one after the other (not inside of each other) - I've simplified the code a bit so that only the important parts of it are listed below. The problem arises when I compare the two echoed queries because the 2nd while loop apparently isn't running at all.
I read somewhere that someone got around the problem by using a for loop for the second one but I want to get down to why exactly the second while loop is not running in my code.
$query_work_title = "SELECT title FROM works WHERE ";
while ($row = mysql_fetch_assoc($result_work_id)) {
$query_work_title .= "OR '$work_id' ";
}
echo $query_work_title;
echo '<br />';
$result_work_title = mysql_query($query_work_title) or
die(mysql_error($cn));
// retrieve the authors for each work in the following query
$query_author_id = "SELECT author_id FROM works_and_authors WHERE ";
while ($row = mysql_fetch_assoc($result_work_id)) {
$query_author_id .= "work_id = 'hello' ";
}
echo $query_author_id;
The MySQL extension keeps track of an internal row pointer for each result. It increments this pointer after each call to mysql_fetch_assoc(), and is what allows you to use a while loop without specifying when to stop. If you intend on looping through a result set more than once, you need to reset this internal row pointer back to 0.
To do this, you would mysql_data_seek() after the first loop:
while ($row = mysql_fetch_assoc($result_work_id)) {
$query_work_title .= "OR '$work_id' ";
}
mysql_data_seek($result_work_id, 0);
You've already looped through the result rows, so it's at the end and returns FALSE. (That's why it exited the loop the first time.)
To reset the internal pointer to the beginning of the result set, use mysql_data_seek().
mysql_data_seek($result_work_id, 0);
After the first while() loop completes, the internal pointer in the MySQL result is at the end of itself. You need to tell it to go back to the beginning using mysql_data_seek() between the first and second loops:
mysql_data_seek($result_work_id, 0);
You have already reached the end of your result set, but you can use mysql_data_seek to reset it.
// query your database
$result = mysql_query(...);
// loop through results
while(($row = mysql_fetch_assoc($result))) {
}
// reset result set
mysql_data_seek($result,0);
// loop again
while(($row = mysql_fetch_assoc($result))) {
}
According to your posted code, your SQL will look something like:
SELECT title FROM works WHERE OR '1'
That query will result in an error so your script shouldn't be getting past that point.
Even if it does, your second loop:
while ($row = mysql_fetch_assoc($result_work_id))
is using a result handle that has already been completely iterated by the first loop. By the the time the second loop tries to use it, mysql_fetch_assoc will return FALSE because there are no more rows to fetch. This will cause the second loop to exit immediately.
If both while loops need to access the same rows, combine their logic so the rows only need to be iterated over one time.
mysql_fetch_assoc steps through the results, right? It's already at the end on the second while loop, so it does nothing.
Related
I have found some PHP code that connects to a MySQL database and gets the column CityName for each row of the table Cities. I'm curious why while() loop is used and not for() or foreach. So here are my questions regarding how while() works in case of looping through arrays:
First, isn't $row variable an 2D array which it's rows contains the list of cities from the SQL query and it's columns contain the columns of each row of the query?
If this is the case, couldn't for() or foreach() be used to loop through the $row array?
Second, how does while() know when the array ends using only $row = $stmt->fetch_assoc() in the while()'s first line in order to end the loop?
Third, how does while() move to the next row of the $row array without using next() at the end of the loop?
And last but not least, how does echo $row['CityName']; output the city name of each row of the $row array without specifying the row of the array to use but only it's column CityName?
Thanks for any answers.
$query="SELECT CityName FROM Cities";
if($stmt = $connection->query("$query"))
{
while ($row = $stmt->fetch_assoc())
{
echo $row['CityName'];
}
}
else
{
echo $connection->error;
}
You could loop through $row, because it is an array (a simple array, not a 2d array); but you're not looping through an array called $row with the while, you're iterating over the resultset returned by $stmt->fetch_assoc() - which isn't an array- and assigning the value of a single returned row to $row in that statement (note the = for assignment) from the resultset.
while itself doesn't magically move any pointer; it's the call to $stmt->fetch_assoc() that not only returns a single row result, but moves the resultset pointer to the next result (and determines when it has reached the end of the resultset)
while($this_is_true){
// do this
}
# if the condition is not true for this while stmt, it will not execute (not even once)
do{
// stuff here
} while($thisIsTrue);
# even if the condition isn't true, a do-while executes at least once (the first time)
The point is that they are using a while() because it's a lazy way to say "only do it if it's true", so they don't have to check if it's true before they loop through it. WIth a for loop, you need to know the number of results you are looping through. For a do, you need to know that there are results before you attempt to use the results (echo). So, you use a while() to check if it's valid and then execute it, with just that piece of code.
Personally, I like to do...
if($query->num_rows > 0){
$query->bind_result($bindvar);
while($query->fetch()){
// do stuff
}
$query->close();
} else {
// no results found
}
You can use foreach instead of while. At least if stmt is a mysqli_result and you PHP version is not terribly outdated (newer than 5.4). In that case your loop would be:
foreach($stmt as $row)
{
echo $row['CityName'];
}
But this does not mean that $stmt is a 2D array. It's an object that implements the Traversable interface. That means that the foreach loop will implicitly invoke the required methods.
I am trying to run a query to my mysql database through php and and am trying to get all the resulting rows. I also have to compare every row to the next row returned. I am trying to do this by setting the result variable to another temporary variable and calling mysqli_fetch_assoc() on that so that the while loop runs again for the next row. But what happens is that when I try to use mysqli_fetch_assoc() even on the other variables, somehow mysqli_fetch_assoc($result) also progresses to the next of the next row when while($row = mysqli_fetch_assoc($result)) goes to next iteration.
Here is the code example to illustrate this :
$query = "SELECT * FROM records ORDER BY num ASC;";
if($result = mysqli_query($conn, $query))
{
while($row = mysqli_fetch_assoc($result))
{
$temporaryresult = $result;
$rowtwo = mysqli_fetch_assoc($temporaryresult);// this makes mysqli_fetch_assoc($result) skip the next row which is unwanted
}
}
So how can I keep mysqli_fetch_assoc($result) from moving forward when I call mysqli_fetch_assoc($temporaryresult) ?
Any help would be appreciated.
am trying to do this by setting the result variable to another temporary variable and calling mysqli_fetch_assoc() on that so that the while loop runs again for the next row
It doesn’t work that way. Just because you assigned the resource id to a second variable, doesn’t mean that you now have a second result set that you could operate on separately. Both variables refer to the same resource id. Fetching a row will still move the row pointer of the “original” data set.
I also have to compare every row to the next row returned
Most likely, you are making things harder on yourself by trying to look ahead. Stuff like this is usually easier done when you look at the previous row instead. That one you have fetched already - so you don’t need to do an additional fetch now that would mess with the row pointer.
Pseudo code example:
$prevRow = null;
while($row = fetch(...)) {
if($prevRow) { // for the first row, this will still be null, so we only
// start comparing stuff when that is not the case
// compare whatever you need to compare here
}
...
$prevRow = $row;
}
After #CBroe's answer, I tried to solve this problem while still trying to look forward. I achieved this by storing the rows returned by the database and then looping through them. This makes it very easy too look ahead in the rows returned while avoiding the complexity of changing your code to look backwards.
$array = array();
// look through query
while($row = mysql_fetch_assoc($query)){
// add each row returned into an array
$array[] = $row;
}
Now, looping through these rows,
$i = 0;
for(;$i<count($array)-1;$i++)
{
if($array[$i]['somecolumn']==$array[$i+1]['anothercolumn'])//compare this column to another column in the next row
{
// do something
}
}
This successfully solved my problem. I hope it helps anyone stuck in the same position I was in.
I am trying to put the entire results set into an array as follows:
while($myres[]=$result->fetch_array(MYSQLI_ASSOC));
This works ok but if I have 5 rows returns in my results the array has 6 index the last being null. So it seems that my array is always one index too big.
I could use num_rows to loop the results but this requires me setting up my own counter and incrementing it, I like the shorthand efficiency of my line above but how to stop it populating the last index with a null set.
This is an alternative to me using fetch_all which I discovered requires a special driver which not all php servers have installed.
I would just use multiple lines
$myres = array();
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
$myres[] = $row;
However, if you have to have it without the extra array assignment line
while ($row = $result->fetch_array(MYSQLI_ASSOC) && $myres[] = $row) {
but that is more or less the same.
The expression
while($myres[]=$result->fetch_array(MYSQLI_ASSOC));
does the following:
execute $result->fetch_array(MYSQLI_ASSOC)
assign the result of this expression to $myres[], thereby appending an entry to the $myres array
pass the result of $result->fetch_array(MYSQLI_ASSOC) as a condition to the while statement and determine whether the loop should be exited.
Notice that appending an entry to the $myres array happens before determining if the while loop should be exited. This is where your extra row comes from.
You will not lose any efficiency by doing it the classical way:
while ($record = $result->fetch_array(MYSQLI_ASSOC)) {
$myres[] = $record;
}
I am retrieving a couple of tables from a MSSQL database, which I am then running through to obtain order information from.
My code looks like this:
while($row = sqlsrv_fetch_array($orderResult))
{
......code........
..................
while($statusRow = sqlsrv_fetch_array($statusResult))
{
....code....
}
....code....
}
Now my problem is that after the second loop runs through, it never runs again. And I need it to run every time the first loop runs.
Is there anything I can do to reset that second loop to run again?
Thank you in advance. Any help or a push in the right direction will be very helpful.
Read about the other parameters in sqlsrv_fetch_array()
You can do something like this to reset
// reset, and get first row
$row = sqlsrv_fetch_row($result, SQLSRV_FETCH_BOTH, SQLSRV_SCROLL_FIRST);
// get second (and nth row) normally
$row = sqlsrv_fetch_row($result);
Alternatively, I think you could benefit from doing a JOIN in your query to return a single result. Merging results manually like this seems a little hackish.
I had a similar problem when calling a stored proc from a database that returned multiple result sets. I found Macek's answer didn't work for me, but another answer did:
$resultSet = array();
$isNotLastResult = true;
$i = 0;
while (!is_null($isNotLastResult))
{
$resultSet[$i] = array();
while ($row = sqlsrv_fetch_array($result, SQLSRV_FETCH_ASSOC))
{
$resultSet[$i][] = $row;
}
$isNotLastResult = sqlsrv_next_result($result);
$i++;
}
print_r($resultSet);
PS: I gave you an up arrow to counteract your down arrow. You asked a question I spent quite a bit of time looking for the answer to. Good question!
Use the following to reset the pointer:
sqlsrv_fetch_array ($Res, SQLSRV_FETCH_ASSOC, SQLSRV_SCROLL_ABSOLUTE, -1);
Then use the same code as before to loop through the result set. sql_fetch_array appears to increment the pointer after retrieving the data. Requesting the -1 record retrieves nothing but then sets the pointer to 0 which is the first record.
are you sure your second loop got results for a second run ? maybe, if your $statusResult depents on your $row and your $row has just entries for the first loop you should insert some pseudo-entries to your db.
the second loop should refresh itself in the next "big" loop cause the whole inner block will be destroyed at the end of the big block and will be (fully) rebuild at the next entry.
Okay, so I realize that when do:
//connection code;
//query code;
//$result= mysqli_query();
$row= mysqli_fetch_array($result);
you create an associative array where the column names from your table are the keys for the data in the respective row.
Then you can use:
while ($row= mysqli_fetch_array($result))
{
//code to echo out table data.
}
My question is how does the while loop go to the next row after each iteration? I thought that was what foreach loops were for?
From http://www.php.net/manual/en/function.mysql-fetch-array.php
array mysql_fetch_array ( resource $result [, int $result_type = MYSQL_BOTH ] )
Returns an array that corresponds to the fetched row and moves the internal data pointer ahead.
Many functions that return a result set do so by returning an array that you can do a foreach() on like you are used to. This is not always the case however, especially with database functions. mysqli_fetch_array fetches just a single row, or returns boolean false if there are no more remaining. This is how the loop works: the expression evaluates to true as long as there is a row to process.
The reason for this construction is mainly efficiency. Fetching database rows can be a performance critical operation, and there are cases where not all rows are needed. In these situations this approach will give more flexibility. Fetching rows one-by-one is also more memory-friendly since not all result data will have to be loaded into memory at once.
Mysqli actually has a function that does fetch the entire result set in an array: mysqli_fetch_all. You will be able to foreach() over that.
mysql_fetch_array simply fetches the next row of the result set from your mysql query and returns the row as an array or false if there are no more rows to fetch.
The while loops continually pulls the results, one at a time from the result set and continues until mysql_fetch_array is false.
A foreach loop loops through each value of an array. As mysql_fetch_array only pulls one result and therefore the value of count($row) would be 1 every time.
Each time the while loop runs, it executes the function mysql_fetch_array and gets the next result. It does that until there aren't more results to show.
mysql_fetch_array returns an array of strings that corresponds to the fetched row, or FALSE if there are no more rows. If row exists then get data.
I hope this has answered you q. Its hard to understand what you mean
This part fetches one row at a time
$row = mysqli_fetch_array($result);
Putting it into a while loop makes it fetch one row at a time, until it does not fetch a row because there are no more to be fetched.
The alternative would be to fetch all the rows, then loop through them with a foreach
$rows = mysql_fetch_all($result);
foreach($rows as $row){
// do something with row
}
For this to work, you have to make yourself a mysql_fetch_all function, which of course has the original while loop in it...
function mysql_fetch_all($result)
{
$all = array();
while($thing = mysql_fetch_assoc($result)) {
$all[] = $thing;
}
return $all;
}
This works due to the SQL connector storing the current state of the query (i.e. the next result row to return) inside the result.
If you want a similar example, it works like reading from a file, where you're able to use similar constructs:
while ($line = fgets($fp, 1000)) {
// ...
}
Behind the scenes (and depending on the language, interpreter, compiler, etc.) for and while essentially result in the same code. The difference is, depending on what your code should do, one approach could be more readable than the other.
Take the following two loops as an example. Both do exactly the same.
for ($i = 0; $i < 10; $i++) {
// ...
}
$i = 0;
while ($i < 10) {
// ...
$i++;
}