This query returns 5 results in phpMyAdmin:
SELECT * FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT 0,5
And this returns count=12 in phpMyAdmin (which is fine, because there are 12 records):
SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT 0,5
This function was working fine before I added the two variables (offset, display), however now it doesn't work, and printing the variables out gives me offset=0, display=5 (so still LIMIT 0,5).
function getProducts($offset, $display) {
$sql = "SELECT * FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT ?,?;";
$data = array((int)$offset, (int)$display);
$rows = dbRowsCount($sql, $data);
logErrors("getProducts(".$offset.",".$display.") returned ".$rows." rows.");
if ($rows > 0) {
dbQuery($sql, $data);
return dbFetchAll();
} else {
return null;
}
}
It's not working because my dbRowsCount(...) method was returning empty string (stupid PDOStatement::fetchColumn), so I changed it to return the count with PDO::FETCH_ASSOC and it returns count=0.
Here is the function which does the row-count:
function dbRowsCount($sql, $data) {
global $db, $query;
$regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/is';
if (preg_match($regex, $sql, $output) > 0) {
$query = $db->prepare("SELECT COUNT(*) AS count FROM {$output[1]}");
logErrors("Regex output: "."SELECT COUNT(*) AS count FROM {$output[1]}");
$query->setFetchMode(PDO::FETCH_ASSOC);
if ($data != null) $query->execute($data); else $query->execute();
if (!$query) {
echo "Oops! There was an error: PDOStatement returned false.";
exit;
}
$result = $query->fetch();
return (int)$result["count"];
} else {
logErrors("Regex did not match: ".$sql);
}
return -1;
}
My error log gives me this output from the program:
Regex output: SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT ?,?;
getProducts(0,5) returned 0 rows.
As you can see, the SQL has not been malformed, and the method input variables were 0 and 5 as expected.
Does anyone know what has gone wrong?
Update
Following a suggestion, I did try to execute the query directly, and it returned the correct result:
function dbDebugTest() {
global $db;
$stmt = $db->query("SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update LIMIT 0,5;");
$result = $stmt->fetch();
$rows = (int)$result["count"];
logErrors("dbDebugTest() returned rows=".$rows);
}
Output:
> dbDebugTest() returned rows=12
Following another suggestion, I changed !=null to !==null, and I also printed out the $data array:
logErrors("Data: ".implode(",",$data));
if ($data !== null) $query->execute($data); else $query->execute();
Output:
> Data: 0,5
However, the dbRowsCount($sql, $data) still returns 0 rows for this query!
Update 2
Following advice to implement a custom PDOStatement class which would allow me to output the query after the values have been binded, I found that the function would stop after $query->execute($data) and so the output would not be printed, although the custom class works for every other query in my program.
Updated code:
function dbRowsCount($sql, $data) {
global $db, $query;
$regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/is';
if (preg_match($regex, $sql, $output) > 0) {
$query = $db->prepare("SELECT COUNT(*) AS count FROM {$output[1]}");
logErrors("Regex output: "."SELECT COUNT(*) AS count FROM {$output[1]}");
$query->setFetchMode(PDO::FETCH_ASSOC);
logErrors("Data: ".implode(",",$data));
$query->execute($data);
logErrors("queryString:".$query->queryString);
logErrors("_debugQuery():".$query->_debugQuery());
if (!$query) {
echo "Oops! There was an error: PDOStatement returned false.";
exit;
}
$result = $query->fetch();
return (int)$result["count"];
} else {
logErrors("Regex did not match: ".$sql);
}
return -1;
}
Output:
Regex output: SELECT COUNT() AS count FROM tbl_product_category WHERE id=?;
Data: 5
queryString:SELECT COUNT() AS count FROM tbl_product_category WHERE id=?;
_debugQuery():SELECT COUNT(*) AS count FROM tbl_product_category WHERE id=?;
Regex output: SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT ?,?;
Data: 0,5
// function stopped and _debugQuery couldn't be output
Update 3
Since I couldn't get the custom PDOStatement class to give me some output, I thought I'd rewrite the getProducts(...) class to bind the params with named placeholders instead.
function getProducts($offset, $display) {
$sql = "SELECT * FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT :offset, :display;";
$data = array(':offset'=>$offset, ':display'=>$display);
$rows = dbRowsCount($sql, $data);
logErrors("getProducts(".$offset.",".$display.") returned ".$rows." rows.");
if ($rows > 0) {
dbQuery($sql, $data);
return dbFetchAll();
} else {
return null;
}
}
Output:
Regex output: SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT :offset, :display;
Data: 0,5
// Still crashes after $query->execute($data) and so logErrors("getProducts(".$offset."...)) wasn't printed out
Update 4
This dbDebugTest previously worked with declaring the limit values 0,5 directly in the SQL string. Now I've updated it to bind the parameters properly:
function dbDebugTest($offset, $display) {
logErrors("Beginning dbDebugTest()");
global $db;
$stmt = $db->prepare("SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update LIMIT :offset,:display;");
$stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
$stmt->bindParam(':display', $display, PDO::PARAM_INT);
if ($stmt->execute()) {
$result = $stmt->fetch();
$rows = (int)$result["count"];
logErrors("dbDebugTest() returned rows=".$rows);
} else {
logErrors("dbDebugTest() failed!");
}
}
The function crashes, and only this is output:
Beginning dbDebugTest()
Update 5
Following a suggestion to turn errors on (they are off by default), I did this:
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
That itself, made the dbDebugTest() from Update 4 work!
Beginning dbDebugTest()
dbDebugTest() returned rows=12
And now an error is generated in my webserver logs:
[warn] mod_fcgid: stderr: PHP Fatal error:
Uncaught exception 'PDOException' with message 'SQLSTATE[42000]:
Syntax error or access violation: 1064 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 ''0', '5'' at line 1'
in /home/linweb09/b/example.com-1050548206/user/my_program/database/dal.php:36
Line 36 refers to dbRowsCount(...) method and the line is $query->execute($data).
So the other method getProducts(...) still doesn't work because it uses this method of binding the data and the params turn into ''0' and '5'' (is this a bug?). A bit annoying but I'll have to create a new method in my dal.php to allow myself to bind the params in the stricter way - with bindParam.
Thanks especially to #Travesty3 and #eggyal for their help!! Much, much appreciated.
Based on Update 2 in the question, where execution stops after the execute statement, it looks like the query is failing. After looking at some PDO documentation, it looks like the default error handling setting is PDO::ERRMODE_SILENT, which would result in the behavior you are seeing.
This is most likely due to the numbers in your LIMIT clause being put into single-quotes when passed in as parameters, as was happening in this post.
The solution to that post was to specify the parameters as integers, using the bindValue method. So you will probably have to do something similar.
And it looks like you should also be executing your queries with try-catch blocks in order to catch the MySQL error.
bindValue method:
if ($data !== null)
{
for ($i=0; $i<count($data); $i++)
$query->bindValue($i+1, $data[$i], PDO::PARAM_INT);
$query->execute($data);
}
else
$query->execute();
You're testing to see if $data is NULL with the equality, rather than the identity, operator (see the PHP manual for more information on how NULL values are handled by the various comparison operators). You need to either use the identity test === / !==, or else call is_null().
As #Travesty3 mentioned above, to test whether an array is empty, use empty().
Related
Order By After Where is not returning results below is the code and screenshot of not running code.
SELECT * from `SalesMessages` where `ThreadId`= ? ORDER BY `id` desc Limit 25;
However if I remove bind params it works fine.
SELECT * from `SalesMessages` where `ThreadId`=63 ORDER BY `id` desc Limit 25;
Below is screen shot from mySql
mysql query and results
HERE IS HOW I WROTE MYSQLI Code
global $conn;
$arr = array();
mysqli_set_charset( $conn, 'utf8'); // ALREADY TRIED COMMENTING THIS LINE
$stmt = $conn->prepare($query);
$stmt->bind_param('i', $ThreadId);
try {
if ($stmt->execute()) {
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
..SOME CODE..
}
if (count($arr) > 0)
return $arr;
} else {
throw "QueryError";
}
} catch (Throwable $e) {
print_r($e);
}
}
Here is the structure of Table
DATA BASE STRUCTURE
I am expecting results from mysql in PHP code as array.
It was connection Error guys Which was using another database which does not had records. Everything else is working fine now. Actually the table in connected database had no record in required table.
I am trying to fetch user
function getItemName($dbh, $userId) {
$itemId = getItemId($dbh, $userId); // the getItemId() function works
echo "item id is: " . $itemId ; // because I can see the correct result if I echo it
$sql = "SELECT name FROM items WHERE id = :item_id";
$stm = $dbh->prepare($sql);
$stm->bindParam(':item_id', $itemId, PDO::PARAM_INT);
$stm->execute();
$result = $stm->fetch();
return $result['name'];
}
And I get Trying to access array offset on value of type bool on the return $result['name']; line.
The field name exists on the items table so that's not the issue.
Also, when I try to further test it, I change the $sql statement to SELECT * FROM items and then when I do echo $stm->rowCount() it finds the correct number of rows (With the original SQL statement row count is 0)
Can't find out what's causing this
I have 3 suggestions:
Make sure to convert $itemId to integer using intval();
Just before returning the function result validate that the query returned results.
$result = $stm->fetch();
if(!$result){
return null;
}
return $result['name'];
Finally, the more obvious, make sure the itemId you are looking for exists in the DB.
I have a mysql query like below
SELECT count(id) as total,
MIN(product_price) as min_price,
MAX(product_price) as max_price
FROM `products_details`
WHERE subcat_id = 425
AND MATCH(alternate_name, product_desc, keywords) AGAINST ('+gas +(>lighter <lighters)' IN BOOLEAN MODE)
This query should be return count value as 7 as per the table records.
But when I executing this query using php in my website it is returning the count value as 59.
When I ran this query directly to phpmyadmin I'm getting correct result.
Can anyone tell why I'm getting this kind of problem and how can I solve this.
Note: When I changed this query like below in when I was ran in phpmyadmin I'm getting the wrong result 59.
SELECT count(id) as total,
MIN(product_price) as min_price,
MAX(product_price) as max_price
FROM `products_details`
WHERE subcat_id = 425
AND MATCH(alternate_name, product_desc, keywords) AGAINST ('+gas +(>lighter <lighters)' IN BOOLEAN MODE)
The method of executing this query using php like below
if (!isset($connection))
die('Database connection is not valid');
$result = #mysql_query($sql, $connection) or die('Could not connect: ' . mysql_error() . ' :: executeQuery');
if (!$result)
return;
if (!#mysql_num_fields($result))
return false;
else {
$result = $result;
}
if (!isset($result))
die('Error in Query');
else {
$ret = null;
if ($object = mysql_fetch_object($result)) {
$ret = $object;
}
mysql_free_result($result);
return $ret;
}
> is the "htmlentity" for >. If > is actually in the query, then it is unlikely to work as expected. Decide where that is coming from and avoid it.
The following code returns me people with similar telephone numbers. It works perfectly but when there are no numbers the function still returns information meaning that I cannot check hide a certain box if there are no other people with similar numbers.
THE FUNCTION
function getothers($tid,$criteria,$telephone,$telephone2,$elector){
global $dbh;
$tid = '-TID'.$tid;
$sql = "SELECT * FROM electors WHERE ((telephone > 0 AND telephone IN ('$telephone','$telephone2')) OR (telephone2 > 0 AND telephone2 IN ('$telephone','$telephone2'))) $criteria AND records NOT RLIKE '$tid' AND ID != '$elector' LIMIT 10";
$result = $dbh->query($sql);
return $result;
}
THE CALL
<?php $others = getothers($post['TID'],$post['criteria'],$elector['telephone'],$elector['telephone2'],$elector['ID']); ?>
THE LINE THAT DOES NOT WORK
<?php if(!$others){?>
$others still has something in it despite no results. I think I might be missing a line in y PDO. Any ideas?
The print_r
PDOStatement Object ( [queryString] => SELECT * FROM electors WHERE ((telephone > 0 AND telephone IN ('02085414023 ','')) OR (telephone2 > 0 AND telephone2 IN ('02085414023 ',''))) AND (this_vi_street = '' AND this_vi_telephone = '') AND (mosaic IN ('A01','A02','A03','A04','A05','A07','B11','C15','C16','C17','C18','H46','J52','K57','K58','K60') OR last_vi IN ('C','P')) AND postal_vote != 1 AND records NOT RLIKE '-TID1' AND ID != '13' LIMIT 10 )
As per the comments, a version using prepared statements:
function getothers($tid, $criteria, $telephone, $telephone2, $elector) {
global $dbh;
$stmt = $dbh->prepare("SELECT *
FROM electors
WHERE ((telephone > 0 AND telephone IN (:telephone, :telephone2))
OR (telephone2 > 0 AND telephone2 IN (:telephone, :telephone2)))
$criteria
AND records NOT RLIKE :tid
AND ID != :elector
LIMIT 10";
$stmt->execute(array(
':telephone' => $telephone,
':telephone2' => $telephone2,
':tid' => '-TID' . $tid,
':elector' => $elector
));
return $stmt->fetchAll();
}
There are still some bad points in this code:
Uses global to get the DB connection, this is overall bad application structure. You should probably use a class or pass $dbh as a regular argument into the function.
Concatenates $criteria into the prepared statement. Do you really need such dynamic conditions that you can't prepare a query for it without concatenating whole SQL blocks into it?
Doesn't necessarily address your actual problem of function returns.
Maybe do something like
$result = $dbh->query($sql);
if($result->rowCount()>0)
{
return $result;
}
return false;
ihave created a simple project to help me get to grips with php and mysql, but have run into a minor issue, i have a working solution but would like to understand why i cannot run this code successfully this way, ill explain:
i have a function,
function fetch_all_movies(){
global $connection;
$query = 'select distinct * FROM `'.TABLE_MOVIE.'` ORDER BY movieName ASC';
$stmt = mysqli_prepare($connection,$query);
mysqli_execute($stmt);
mysqli_stmt_bind_result($stmt,$id,$name,$genre,$date,$year);
while(mysqli_stmt_fetch($stmt)){
$editUrl = "index.php?a=editMovie&movieId=".$id."";
$delUrl = "index.php?a=delMovie&movieId=".$id."";
echo "<tr><td>".$id."</td><td>".$name."</td><td>".$date."</td><td>".get_actors($id)."</td><td>Edit | Delete</td></tr>";
}
}
this fetches all the movies in my db, then i wish to get the count of actors for each film, so i pass in the get_actors($id) function which gets the movie id and then gives me the count of how many actors are realted to a film.
here is the function for that:
function get_actors($movieId){
global $connection;
$query = 'SELECT DISTINCT COUNT(*) FROM `'.TABLE_ACTORS.'` WHERE movieId = "'.$movieId.'"';
$result = mysqli_query($connection,$query);
$row = mysqli_fetch_array($result);
return $row[0];
}
the functions both work perfect when called separately, i just would like to understand when i pass the function inside a function i get this warning:
Warning: mysqli_fetch_array() expects
parameter 1 to be mysqli_result,
boolean given in
/Applications/MAMP/htdocs/movie_db/includes/functions.inc.php
on line 287
could anyone help me understand why?
many thanks.
mysqli_query failed to run your query:
Returns FALSE on failure. For
successful SELECT, SHOW, DESCRIBE or
EXPLAIN queries mysqli_query() will
return a result object. For other
successful queries mysqli_query() will
return TRUE.
Before running mysqli_fetch_array test $result... Something like:
if ($result !== false)
$row = mysqli_fetch_array($result);
else
return false;
Seems like a variable scope issue within your SQL statement. Outputting the SQL should show you the "true" error.
You may want to try using classes with your functions, for example:
class getInfo {
function fetch_all_movies(){
global $connection;
$query = 'select distinct * FROM `'.TABLE_MOVIE.'` ORDER BY movieName ASC';
$stmt = mysqli_prepare($connection,$query);
mysqli_execute($stmt);
mysqli_stmt_bind_result($stmt,$id,$name,$genre,$date,$year);
while(mysqli_stmt_fetch($stmt)){
$editUrl = "index.php?a=editMovie&movieId=".$id."";
$delUrl = "index.php?a=delMovie&movieId=".$id."";
echo "<tr><td>".$id."</td><td>".$name."</td><td>".$date."</td><td>".get_actors($id)."</td><td>Edit | Delete</td></tr>";
}
}
function get_actors($movieId){
global $connection;
$query = 'SELECT DISTINCT COUNT(*) FROM `'.TABLE_ACTORS.'` WHERE movieId = "'.$movieId.'"';
$result = mysqli_query($connection,$query);
$row = mysqli_fetch_array($result);
return $row[0];
}
}
$showInfo = new getInfo;
//fetch all movies
$showInfo->fetch_all_movies();
//List actors from movie 1
$showInfo->get_actors("1");
In case of an error mysqli_query will return false. You have to handle the error a simple way to do this might be:
$result = mysqli_query($connection,$query);
if (!$result) {
die(mysqli_error($connection));
}
$row = mysqli_fetch_array($result);
Please note that terminating (by doing die() ) usually is no good way to react on an error, log it, give the user anice error page etc. It's alsonogood practice to give the low level error message toauser, this might motivate users to try to exploit a possile security issue.
Last remark: you were writing
$query = 'SELECT DISTINCT COUNT(*) FROM `'.TABLE_ACTORS.'` WHERE movieId = "'.$movieId.'"';
you should properly escape the movieId there.