Why does my PHP MYSQL Query not work with SUM? - php

I don't get it.
This PHP - MYSQL query does work:
$sql = mysqli_prepare($conn, 'SELECT O.*
FROM OFFER O
LEFT JOIN EVENT E ON E.OFFER_ID = O.KEY_ID
LEFT JOIN BOOKING B ON B.EVENT_ID = E.KEY_ID
WHERE O.KEY_ID = ? ');
$sql->bind_param('i', $keyId);
With SUM statement it doesn't work:
$sql = mysqli_prepare($conn, 'SELECT O.*,
SUM(CASE WHEN B.KEY_ID IS NULL THEN 0 ELSE 1 END) AS BOOKING_COUNT
FROM OFFER O
LEFT JOIN EVENT E ON E.OFFER_ID = O.KEY_ID
LEFT JOIN BOOKING B ON B.EVENT_ID = E.KEY_ID
WHERE O.KEY_ID = ? ');
$sql->bind_param('i', $keyId);
Error message:
Uncaught Error: Call to a member function bind_param() on boolean in ..snippet-ops.php(361) : eval()'d code:107
The query works in phpmyadmin though.Does anyone know why?
EDIT: SOLVED: The accepted Answer contains the solution in the comments (turning on report and a GROUP BY solved the issue.
EDIT2: When downgrading a question it would be good to know why otherwise the downgrade is useless.

Try surrounding your query string with double quotes instead of single quotes. Beware, that prepare might return false instead of a statement object. You should check this and react accordingly. Also make sure to close your statement after you're done with it.
Here (not tested):
<?php
$sql = "
SELECT O.*, SUM(CASE WHEN B.KEY_ID IS NULL THEN 0 ELSE 1 END) AS BOOKING_COUNT
FROM OFFER O
LEFT JOIN EVENT E ON E.OFFER_ID = O.KEY_ID
LEFT JOIN BOOKING B ON B.EVENT_ID = E.KEY_ID
WHERE O.KEY_ID = ?
GROUP BY O.KEY_ID";
if ($stmt = $conn->prepare($sql)) {
$stmt->bind_param("i", $keyId);
$stmt->execute();
// do stuff with $stmt
$stmt->close();
} else {
echo $stmt->error;
}
?>
UPDATE: As can be seen in the comments, it was necessary to group by the column O.KEY_ID, or the prepare would return false.

Related

How to resolve quote issue in mysql php prepare statement

I have a mysql prepare statement in a PHP script as such:
$stmt = $ln_sph->prepare("SELECT (CASE WHEN n.Id IS NULL THEN e.ExclusionEn ELSE concat(e.ExclusionEn, ' (', n.TitleEn, ' (', n.Naics, '))') END) AS Exclusion
FROM tblExclusion e
LEFT JOIN tblNaics n ON (e.ExclusionClassNaicsId = n.Id)
WHERE NaicsId = :Id");
$stmt->bindValue(':match', $search_query, PDO::PARAM_STR);
$stmt->execute();
$docs = $stmt->fetchAll();
I would like to modify query in the prepare statement to handle this query...
SELECT (CASE WHEN n.Id IS NULL THEN e.ExclusionEn ELSE concat(e.ExclusionEn, ' (','(',n.TitleEn, ' (', n.Naics, '))') END) AS Exclusion
FROM tblExclusion e
LEFT JOIN tblNaics n ON (e.ExclusionClassNaicsId = n.Id)
WHERE NaicsId = :Id
I can run this query just fine directly within mysql but I don't know how to get it in the prepare statement due to the quotes. I think the bindValue might be of use but have not had any luck with it. Any help or point in the right direction would be appreciated.
First break up the SQL into a separate variable. Makes the code cleaner and easier to debug:
$sql = 'SELECT (CASE WHEN n.`Id` IS NULL THEN e.`ExclusionEn` ELSE concat(e.`ExclusionEn`, \' (\',\'(\',n.`TitleEn`, \' (\', n.`Naics`, \'))\') END) AS Exclusion
FROM `tblExclusion` AS e
LEFT JOIN `tblNaics` AS n ON (e.`ExclusionClassNaicsId` = n.`Id`)
WHERE `NaicsId` = :Id';
then you can var_dump to see how it comes out for use and see where it goes wrong.
Other note: You can replace \' with " - but I'm a single-quote fan :)
After that, chuck it into the prepare statement:
$stmt = $ln_sph->prepare($sql);

MySQLi: Select price range if empty inputs

I have 2 variables to define a price range for a query. The problem I'm trying to solve is when these are not set in which case I want to show all rows (from 1, if the lower boundary is null, and to max(price) if the upper boundary is null).
I've tried with ifnull, but without success.
$priceFrom = $_POST['priceFrom'];
$priceTo = $_POST['priceTo'];
if(is_null($priceFrom) || is_null($priceTo)){
$priceFrom = 0;
$priceTo = 0;
}
$mass = array();
foreach($data as $current){
$sql = "SELECT p.price,
p.type,
p.area,
p.floor,
p.construction,
p.id as propertyID,
CONCAT(u.name, ' ',u.family) as bname,
p.type as ptype,
n.name as neighborhoodName,
CONCAT(o.name,' ',o.surname,' ',o.family) as fullName
FROM `property` p
LEFT JOIN `neighbour` n ON p.neighbour = n.id
RIGHT JOIN `owners` o ON p.owner = o.id
LEFT JOIN users u ON p.broker = u.id
WHERE `neighbour`= '$current'
AND `price` BETWEEN ifnull('$priceFrom', '1') AND ifnull('$priceTo','2000000')
";}
SQL INJECTION
^ Please Google that! Your code is seriously vulnerable! Your data can be stolen or deleted...
You have to sanitize your inputs at least with mysqli_real_escape_string()
Even better would be to take proper countermeasures to SQL injection and use prepared statements and parametrized queries! (as shown in the code below)
I think the best approach would be to handle the logic by altering the query based on the values of the variables:
$sql = "SELECT p.price,
p.type,
p.area,
p.floor,
p.construction,
p.id as propertyID,
CONCAT(u.name, ' ',u.family) as bname,
p.type as ptype,
n.name as neighborhoodName,
CONCAT(o.name,' ',o.surname,' ',o.family) as fullName
FROM `property` p
LEFT JOIN `neighbour` n ON p.neighbour = n.id
RIGHT JOIN `owners` o ON p.owner = o.id
LEFT JOIN users u ON p.broker = u.id
WHERE `neighbour`= :current "; //note: ending white space is recommended
//lower boundary clause -- if variable null - no restriction
if(!is_null($priceFrom){
sql = sql . " AND `price` >= :priceFrom "; // note: whitespace at end and beginning recommended
}
//upper boundary -- better than to set it to an arbitrary "high" value
if(!is_null($priceTo)){
sql = sql . " AND `price` <= :priceTo "; // note: whitespace at end and beginning recommended
}
This approach allows for any upper value: if there is a serious inflation, a different currency, or suddenly the code will be used to sell housese and there will be products with prices > 200000, you don't need to go out and change a lot of code to make it show...
The parameters need to be bound when executing the query of course:
$stmt = $dbConnection->prepare(sql);
$stmt->bind_param('current', $current);
if(!is_null($priceFrom)){
$stmt->bind_param('priceFrom', $priceFrom);
}
if(!is_null($priceTo)){
$stmt->bind_param('priceTo', $priceTo);
}
//execute and process in same way
$stmt->execute();
Also note: from your code it seems you are issuing queries in a loop. That is bad practice. If the data on which you loop comes
from the DB --> use a JOIN
from an array or other place of the code --> better use an IN clause for the elements
to fetch all data with one query. This helps a lot both in organizing and maintaining the code and results generally in better performance for the most cases.

What am I doing wrong in this query?

There are three tables,
storing user details
storing groups details
storing user and group ids.
I need to check if a user is already a member of one group. I'm using this query to achieve that:
SELECT u.id, g.id
FROM users u, groups g
INNER JOIN user_groups ug
ON ug.user_id = u.id AND ug.group_id = g.id
WHERE ug.user_id = ? AND ug.group_id = ?
but this is throwing me an error:
Call to a member function bind_param() on boolean in
I have checked if i have misspelled some word in my query and everything is okay.
EDIT:
Here is a function:
public function isUserMember($user_id, $group_id) {
$stmt = $this->conn->prepare("
SELECT u.id, g.id from users u, groups g
INNER JOIN user_groups ug
ON ug.user_id = u.id AND ug.group_id = g.id
WHERE ug.user_id = ? AND ug.group_id = ?");
$stmt->bind_param("ii", $user_id, $group_id); // here i'm getting an error
$stmt->execute();
$stmt->store_result();
$num_rows = $stmt->num_rows;
$stmt->close();
return $num_rows > 0;
}
Try following changes
removed unnecessary join with user table because if you have user_id and group_id you don't need to join it with user
SELECT ug.id, g.id
from user_groups ug
inner join groups g
on ug.group_id = g.id
WHERE ug.user_id = ? AND ug.group_id = ?
Your specific issue Error statement is:
Call to a member function bind_param() on boolean in
at:
$stmt->bind_param("ii", $user_id, $group_id);
What this means is that there is no function bind_param() on boolean (true or false) , so this means your $stmt is a boolean, meaning the statement defining line has returned FALSE.
so:
$stmt = $this->conn->prepare("
SELECT u.id, g.id from users u, groups g
INNER JOIN user_groups ug
ON ug.user_id = u.id AND ug.group_id = g.id
WHERE ug.user_id = ? AND ug.group_id = ?");
This is where the problem is. Others in comments have stated that your SQL query is incorrect, which would result in a boolean fail, however, if it is not your SQL query itself that is failing, then you would need to establish that the $this->conn value has been successfully generated and that it is a valid object entity.
Try to output an error log something like:
if(!$stmt = $this->conn->prepare($sql)){
$errorDump = '
Error 1 '.date("r").' :
';
$errorDump .= $this->conn->error;
$errorDump .= "\n\nBacktrace:\n".print_r(debug_backtrace(),TRUE);
$errorDump .= "
SQL: ".$sql;
error_log($errorDump);
unset($errorDump);
return false;
}
....
//carry on with the query as it's ok
The above is a bit quick and dirty but when your $stmt returns false this error report will tell you why. You can then use that error information to solve your Query or your PHP variable structure.

Query works on Sequel Pro but not on my php script

This query works fine on Sequel Pro:
SELECT t1.* FROM `erapido_messages` t1
LEFT OUTER JOIN `erapido_messages` t2 ON `t1.sender_id` = `t2.sender_id`
AND (`t1.msg_date` < `t2.msg_date` OR `t1.msg_date` = `t2.msg_date` AND `t1.sender_id` != `t2.sender_id`)
WHERE `t2.sender_id` IS NULL AND `t1.sender_id`!= `0` AND `t1.receiver_id`= 28
ORDER BY `t1.msg_date` DESC;
When I use it on my php script it returns an error. This is the complete query in php:
$query = "SELECT t1.* FROM `erapido_messages` t1
LEFT OUTER JOIN `erapido_messages` t2 ON `t1.sender_id` = `t2.sender_id`
AND (`t1.msg_date` < `t2.msg_date` OR `t1.msg_date` = `t2.msg_date` AND `t1.sender_id` != `t2.sender_id`)
WHERE `t2.sender_id` IS NULL AND `t1.sender_id`!= `0` AND `t1.receiver_id`= ?
ORDER BY `t1.msg_date` DESC";
//$sql is my connection and it works fine on other queries
$statement = $sql->prepare($query);
//bind parameters for markers: s = string, i = integer, d = double, b = blob
$statement->bind_param('i', $receiver_id);//$receiver_id is defined
//execute query
$statement->execute();
//store the results; allows to count the rows
$statement->store_result();
//bind result variables
$statement->bind_result($id, $receiver_name, $receiver_img, $receiver_email, $sender_id, $sender_name, $sender_email, $sender_img, $subject, $message, $msg_date);
This is the error:
Fatal error: Call to a member function bind_param() on boolean in /messages.php on line 53
I understand that this statement may return 'false' if the query fails:
$statement = $sql->prepare($query);
However, I can't see what is wrong in the query. Any help is welcome!
Thanks much.
Your prepare statement is returning false due to not valid query string. Change your query like the one below. Currently you are escaping your column names because they are in back-tick which results in an error while preparing it
$query = "select * from `dbname` table where `table`.column= ?
ORDER BY `table`.column DESC LIMIT 2 ";
That should fix this error.

php mysql select something and get columns from other tables

I'm using prepared statements and I need to "select" other table, apart from these two, to get data but I get this:
Fatal error: Call to a member function bind_param() on a non-object in C:\xampp\htdocs\views\user\referral.php on line 16
If I add in SELECT table1.* , table.* , "theothertable.*"
$stmt = $mysqli->prepare("SELECT friends.*, rc_usuario.* // or just *
FROM friends
INNER JOIN rc_usuario ON rc_usuario.id = friends.friendID
WHERE friends.userID = ?");
$stmt->bind_param('s', $connectedUserID);
This is working fine, I get what i need, but I also need to get data from another table and I can't make other select because i need it all in a while to print all the data together.
The question is, can I SELECT something like that from 2 tables and also get data from other table/s?
Thank YOU!
EDIT: Add the new statement:
if ($stmt = $mysqli->prepare("SELECT friends.*, members.*, account_type.*
FROM friends
INNER JOIN members ON members.id = friends.friendID
INNER JOIN account_type ON account_type.name = members.acc_type
WHERE friends.userID = ? AND members.acc_type = ?")) {
$stmt->bind_param('is', $connectedUserID, $connectedAcc_type);
$stmt->execute();
} else echo $mysqli->error;
You can join more tables by using another INNER JOIN, like as follows;
INNER JOIN rc_usuario ON rc_usuario.id = friends.friendID
INNER JOIN rc_another ON rc_another.col = friends.coljoin
Just make sure you select all the columns you want in the joined table.
It might also help to run your prepare statement in an if, like this;
if($stmt = $mysqli->prepare("SELECT ...")) { // ... where the rest of your query is
$stmt->bind_param('s', $connectedUserID);
$stmt->execute();
}
else {
echo $mysqli->error;
}
which will give you an idea of any problems with the SQL syntax.
Hope this helps.

Categories