PreparedStatement select with php not working as expected - php

I have this query:
SELECT id, result, ip_address, added_date
FROM results
WHERE course_id = (
SELECT id
FROM courses
WHERE course = 'informatica'
AND macro_course_id = (
SELECT id
FROM macro_courses
WHERE macro_course = 'scienze-matematiche-fisiche-e-naturali'
AND organization_id = (
SELECT id
FROM organizations
WHERE organization = 'universita-degli-studi-di-torino'
AND city_id = (
SELECT id
FROM cities
WHERE city = 'torino'
AND region_id = (
SELECT id
FROM regions
WHERE region = 'piemonte' ))))) ORDER BY id DESC
And i'm using this code to do it with a preparedstatement
public function getResults($region, $city, $organization, $macro_course, $course) { //works
//added_date=datetime : YYYY-MM-DD HH:mm:ss
echo "SELECT id, result, ip_address, added_date
FROM results
WHERE course_id = (
SELECT id
FROM courses
WHERE course = '$course'
AND macro_course_id = (
SELECT id
FROM macro_courses
WHERE macro_course = '$macro_course'
AND organization_id = (
SELECT id
FROM organizations
WHERE organization = '$organization'
AND city_id = (
SELECT id
FROM cities
WHERE city = '$city'
AND region_id = (
SELECT id
FROM regions
WHERE region = '$region' ))))) ORDER BY id DESC"; //just for me to know what query is being executed
if ($stmt = $this->mysqli->prepare(("
SELECT id, result, ip_address, added_date
FROM results
WHERE course_id = (
SELECT id
FROM courses
WHERE course = ?
AND macro_course_id = (
SELECT id
FROM macro_courses
WHERE macro_course = ?
AND organization_id = (
SELECT id
FROM organizations
WHERE organization = ?
AND city_id = (
SELECT id
FROM cities
WHERE city = ?
AND region_id = (
SELECT id
FROM regions
WHERE region = ? ))))) ORDER BY id DESC
"))) {
$return = array();
$stmt->bind_param('sssss', $course, $macro_course, $organization, $city, $region);
$stmt->execute();
if ($stmt->fetch()) {
$i = 0;
while ($row = $stmt->fetch()) {
print_r($row);//this is never reached
continue;
$s = new Result($row['result'], $row['added_date'], $row['id']);
$return[$i] = $s;
$i+=1;
}
}
}
return $return;
}
The problem is that this function returns 0 rows and 0 errors (checked with $this->mysqli->error), it seems that $row = $stmt->fetch() is always false.
However, if i copy and execute on PHPMyAdmin the output i get at the function top, i see
Showing lines 0 - 0 ( 1 total, Query time 0.0003 sec)
So the query returns a line but it is not catched by php. What am i missing? How can i fix this?

Because you used $stmt-fetch() two times here
if ($stmt->fetch()) {
$i = 0;
while ($row = $stmt->fetch()) {
Remove the if ($stmt->fetch()) condition, it will work as expected.
EDIT
from docs
Note that all columns must be bound by the application before calling $stmt->fetch().
You have to bind the result before calling $stmt->fetch() like this
/* bind result variables */
$stmt->bind_result($name, $code);

Related

select row after update in mysql PDO

How to select one row after update in one query with PDO and PHP?
like this:
function giftUpdate($username,$id,$date,$conn){
$update = $conn->prepare("UPDATE gifts SET used = 1 , user = :user , date = :date WHERE id = :id");
$update->bindParam("id" , $id );
$update->bindParam("date" , $date );
$update->bindParam("user" , $username );
$update->execute();
return $update ? true : false;
}
function giftGet($username,$currnetTime,$conn) {
$statment = $conn->prepare("SELECT * FROM gifts WHERE used = 0 ORDER BY `id` ASC");
$statment->execute();
$gift = $statment->fetch(PDO::FETCH_OBJ);
if($gift){
$voucher = $gift->code;
$voucherid = $gift->id;
$vouchertype = $gift->type;
$voucherkey = $gift->keys;
if($voucher){
$giftUpdate = giftUpdate($username,$voucherid,$currnetTime,$conn);
if($giftUpdate){
useUpdate($username , $conn);
}
}
}
usleep(50000);
return $gift ? $gift : false;
}
After update I need select the same row to send it to user.
(i`am one table for vouchers list with 1000row and column 'used' with 0 by default , and with user request i need in one query update this row to be used 1 and select this row.)
my problem in this functions is Some users will receive a duplicate vouchers And a code is assigned to several users.
guys ... for this case Is it possible to use the transaction method? Or do you suggest using a transaction at all?
Edited:
I eventually changed the code below, but I'm still in line :
SELECT id FROM gifts WHERE used = 0 ORDER BY id ASC limit 1 FOR UPDATE)");
But when I put an id from the database instead of this code, it works!
function giftGet($username, $currnetTime, $conn)
{
$uniqid = uniqid('vouchers_');
$update = $conn->prepare("UPDATE gifts SET used = 1, uniqueid =:uniqueid , user = :user , date = :date WHERE id = (
SELECT id FROM gifts WHERE used = 0 ORDER BY `id` ASC limit 1 FOR UPDATE)");
$update->bindParam("date", $currnetTime);
$update->bindParam("uniqueid", $uniqid);
$update->bindParam("user", $username);
$update->execute();
$updaterowCount = $update->rowCount();
if ($updaterowCount)
{
$statment = $conn->prepare("SELECT * FROM gifts WHERE uniqueid = :uniqueid ORDER BY `id` ASC");
$statment->bindParam("uniqueid", $uniqid);
$statment->execute();
$gift = $statment->fetch(PDO::FETCH_OBJ);
$voucherid = $gift->id;
if ($gift)
{
useUpdate($username, $voucherid, $conn);
}
}
return $gift ? $gift : false;
}
I would alter the table gifts and add a column uniqueid(varchar).
then use
$uniqid = uniqid('vouchers_');
and rewrite your update
"UPDATE gifts SET used = 1, uniqueid =:UNIQUEID , user = :user , date = :date WHERE id = (
SELECT id FROM gifts WHERE used = 0 ORDER BY `id` ASC limit 1 FOR UPDATE)";
now you can identify the updated record by uniqueid.
In Your Code there is a time-gap between the select and the update. 2 or more users can start at the same time the select, recieve the same voucher before it is updated with used = 1

Count all the rows that match all the values in IN clause

The values of my $topics are 1,2,3 i want to make a select query that is similar to SELECT COUNT(*) FROM subtopics WHERE main_id = 1 AND main_id = 2 and main_id = 3..
function countTopics($mid,$forum){
$topics = implode(', ', array_column($mid, 'main_id'));
parse_str("id=1");
$query = "SELECT COUNT(*)
FROM subtopics
WHERE main_id
IN ($topics)
GROUP BY main_id
HAVING COUNT(DISTINCT $topics) = 3";
$stmt = $forum->prepare($query);
$stmt->execute(array(
'mid' => $topics
));
$rows = $stmt->fetchAll();
$count = implode(', ', array_column($rows, 'COUNT(*)'));
$_SESSION['topics_count'] = $count;
}
There are various problems with the code, your SQL doesn't need a HAVING clause, your trying to bind to a variable which isn't in the SQL statement...
function countTopics($mid,$forum){
$topics = implode(', ', array_column($mid, 'main_id'));
$query = "SELECT main_id, COUNT(*) as countID
FROM subtopics
WHERE main_id IN ($topics)
GROUP BY main_id";
if ( $stmt = $forum->prepare($query) ) {
if ( $stmt->execute() ) {
$rows = $stmt->fetchAll();
$count = implode(', ', array_column($rows, 'countID'));
$_SESSION['topics_count'] = $count;
}
}
}
Also you should check if the previous stage has worked - so 'if the statement prepare has worked' etc. Not sure what you would do if any of these fail, but that is down to your application design.
Not surewhat $forum is as it doesn't seem to be used and it would be more normal to return the value of the count and let the calling code decide what to do with the value.
You probably want to do something like...
$rows = $stmt->fetchAll();
$_SESSION['topics_count'] = $rows;
and then later...
foreach ( $_SESSION['topics_count'] as $topic ) {
echo $topic['main_id'].'-'.$topic['countID'].PHP_EOL;
}
Update:
function countTopics($mid,$forum){
$topics = implode(', ', array_column($mid, 'main_id'));
$query = "SELECT main_id, subtopic_id, COUNT(*) as countID
FROM subtopics
WHERE main_id IN ($topics)
GROUP BY main_id, subtopic_id";
if ( $stmt = $forum->prepare($query) ) {
if ( $stmt->execute() ) {
$rows = $stmt->fetchAll();
$_SESSION['topics_count'] = $rows;
}
}
}
I've added a column called 'subtopic_id', this may need to be changed depending on your database column name. This will give rows as something like...
main_id subtopic_id countID
1 1 12
1 2 6
1 3 10
2 6 1
2 11 3
This means you will have multiple rows for each main_id, but they will have each sub topic and a count for that subtopic. If you want to output the data - you will have to use a loop, like the foreach above.

Is it possible to loop through the mysql table rows and check columns?

I have a "matrix" table with the following columns filled in.
matrix_id, user_id, position_1, position_2, position_3
1 1 1982 2251 5841
2 2 6204 0 0
3 3 0 0 0
4 4 0 0 0
I basically want to do the following.
Find a row with the lowest user_id and that has an empty position.
In the example above, that would be user_id 2 and position_2.
I update the row with a query.
I then move on to the next next empty position. Since user_id 2 still has an empty position_3, I update the row again with a query.
Since that row is complete, I move on to the next highest user_Id that has empty positions. In this case, it's user_id 3 and then user_id 4 the one after that.
I know I can do all of the above if I know what the user_id is. But assume in this case, I have no clue what the user_ids are. How would the queries look then?
Here is what I have so far.
$find_user = $db->prepare("SELECT * FROM matrix WHERE user_id > :user_id");
$find_user->bindValue(':user_id', 0);
$find_user->execute();
$result_user = $find_user->fetchAll(PDO::FETCH_ASSOC);
if(count($result_user) > 0) {
foreach($result_user as $row) {
$matrix_id = $row['matrix_id'];
$user_id = $row['user_id'];
$position_1 = $row['position_1'];
$position_2 = $row['position_2'];
$position_3 = $row['position_3'];
}
} else {
$errors[] = 'User Id not found in Matrix.';
}
$update_user = $db->prepare("UPDATE matrix SET position_2 = :position_2 WHERE user_id = :user_id");
$update_user->bindValue(':position_2', 1564;
$update_user->bindParam(':user_id', $user_id);
if($update_user->execute()) {}
This should go through all your users from smallest user_id to largest.
For each user it will check the relevant columns in order and apply a new value to the empty ones.
$new_val = 1999;
$result = $db->query("SELECT * FROM matrix ORDER BY user_id");
$users = $result->fetchAll(PDO::FETCH_ASSOC);
if(count($users) > 0) {
// prepare all the possible queries
// make use of prepare once execute many times
$stmt1 = $db->prepare("UPDATE `matrix` SET `position_1` = :pos WHERE `user_id` = :id");
$stmt2 = $db->prepare("UPDATE `matrix` SET `position_2` = :pos WHERE `user_id` = :id");
$stmt3 = $db->prepare("UPDATE `matrix` SET `position_3` = :pos WHERE `user_id` = :id");
foreach($users as $user) {
if ( $user['$position_1'] == 0 ) {
$stmt1->execute( array(':pos'=>++$new_val,':id'=>$user['user_id']) );
}
if ( $user['$position_2'] == 0 ) {
$stmt1->execute( array(':pos'=>++$new_val,':id'=>$user['user_id']) );
}
if ( $user['$position_3'] == 0 ) {
$stmt1->execute( array(':pos'=>++$new_val,':id'=>$user['user_id']) );
}
}
} else {
$errors[] = 'User Id not found in Matrix.';
}
You could reduce the rows to process by changing the query a bit to only find users with columns to fix
$result = $db->query("SELECT *
FROM matrix
WHERE position_1 = 0
OR position_2 = 0
OR position_3 = 0
ORDER BY user_id");
important thing here, is that you are working with the row, not columns
so check all the prerequisites and update the row.
$find_user = $db->prepare("SELECT * FROM matrix order by user_id asc");
$find_user->execute();
$result_user = $find_user->fetchAll(PDO::FETCH_ASSOC);
foreach($result_user as $row) {
$matrix_id = $row['matrix_id'];
$user_id = $row['user_id'];
$position_1 = $row['position_1'];
$position_2 = $row['position_2'];
$position_3 = $row['position_3'];
}
$str = ''
if (!$position_2){
$str = "position_2 = :position_2"
} else if (!$position_2 && !$position_3){
$str = "position_2 = :position_2 and position_3 = :position_3"
}
$update_user = $db->prepare("UPDATE matrix SET " . $str . " WHERE user_id = :user_id");
$update_user->bindValue(':position_2', 1564);
$update_user->bindValue(':position_3', 1564);
$update_user->bindParam(':user_id', $user_id);
if($update_user->execute()) {}
Also, get all the rows in the matrix table ordered by used_id and process every row, depending on your condition.

Two tables, display multiple results from one matching the ID

I have two tables:
post_languages with the following columns: languageID, languageName
post_to_languages with: postID, postLanguage
What I'm trying to achieve is to display all languages associated with a post.
example: Post 1, languages: French, Russian
Maybe my approach is wrong but this is one of the methods I have tried:
//get language id
$stmt2 = $db->prepare('SELECT languageID FROM post_to_languages WHERE postID = :postID');
$stmt2->execute(array(':postID' => $row['postID']));
//Count total number of rows
$rowCount2 = $stmt2->rowCount();
if ($rowCount2 > 0) {
$row2 = $stmt2->fetch(PDO::FETCH_ASSOC);
foreach ($row2 as $langID) {
$stmt3 = $db->prepare('SELECT languageName FROM post_languages WHERE languageID = :languageID');
$stmt3->execute(array(':languageID' => $langID));
$row3 = $stmt3->fetch();
$lang_string = $row3['languageName'];
}
} else {
$lang_string = "Unknown";
}
Doesn't matter what I tried, I get only one language. Maybe I should select post_to_languages by ID first.
You don't need this nested loop, try a join or a subquery. Showing you a subquery. If you have a large number of rows, an inner join will be faster. But a subquery is still heaps faster than a nested loop.
SELECT languageName FROM post_languages WHERE languageID IN
(SELECT languageID FROM post_to_languages WHERE postID = :postID')
If you still don't see any results, replace :postID with a real value and try it in the console.
you are selected different column from diff. table.
in table post_to_language must be same datatype as languageId.
please try this code
//get language id
$stmt2 = $db->prepare('SELECT postLanguage FROM post_to_languages WHERE postID = :postID');
$stmt2->execute(array(':postID'=>$row['postID']));
//Count total number of rows
$rowCount2 = $stmt2->rowCount();
if($rowCount2 > 0){
$row2 = $stmt2->fetch(PDO::FETCH_ASSOC);
foreach($row2 as $langID) {
$stmt3 = $db->prepare('SELECT languageName FROM post_languages WHERE languageID = :languageID');
$stmt3->execute(array(':languageID'=>$langID));
$row3 = $stmt3->fetch();
$lang_string = $row3['languageName'];
}
} else {
$lang_string = "Unknown";
}
Thank you.

how to get all datas in a mysql table with same id

I have something like that in my mySQL database:
(User 734 have many informations : biography, name, phone, mail ...)
I want to get an array (in PHP) with grouped datas :
array(
[734]=>
object {
[155] => string "Dominique",
[4] => int(047682037),
[1] => string "Dominique B"
},
[735]=>
object {
[155] => string "Other",
[4] => int(0123456789),
[1] => string "Other B"
}
)
not only for 734 user but for each user. With a simple query I get everything but not in the good order. Can I make it in SQL or I maybe need to rearrange datas in PHP next ?
What is the sql query to get, for each user_id, all the related datas ?
I can't change the database structure (WP and buddypress)
I can't use native WP functions (because getting datas from another site)
SELECT * FROM (whatever your table name is)
WHERE user_id = (whatever user id you're interested in getting data for)
Solution using ORDER BY :
$users = array();
$current_user = null;
$result = $mysqli->query("SELECT user_id, field_id, value FROM `TABLE_NAME` ORDER BY user_id, field_id");
while ($result && $row = $mysqli->fetch_assoc($result)) {
if ($current_user != $row['user_id']) {
$current_user = $row['user_id'];
$users[$row['user_id']] = array();
}
$users[$row['user_id']][$row['field_id']] = $row['value'];
}
EDIT :
There is another solution using GROUP BY and GROUP_CONCAT :
$users = array();
$result = $mysqli->query("SELECT user_id, GROUP_CONCAT(field_id SEPARATOR '|') as fields, GROUP_CONCAT(value SEPARATOR '|') as values FROM `TABLE_NAME` GROUP BY user_id");
while ($result && $row = $mysqli->fetch_assoc($result)) {
$fields = explode('|', $row['fields']);
$values = explode('|', $row['values']);
$users[$row['user_id']] = array();
// Problem id you have your field ids and your values in separate arrays, not sure what you want to do with them
}
$result = mysqli_query($con, "SELECT * FROM table_name WHERE user_id = 734");
Or if you arent using mysqli:
$result = mysql_query("SELECT * FROM table_name WHERE user_id = 734");
If you are using the PDO library you could check the PDO::FETCH_GROUP attribute.
http://php.net/manual/en/pdostatement.fetchall.php
fetch_style:
To return an associative array grouped by the values of a specified column, bitwise-OR PDO::FETCH_COLUMN with PDO::FETCH_GROUP.
$stmt = $this->_mysqli->prepare('SELECT user_id,field_id,value FROM WHERE user_id = ?');
$stmt->bind_param('i', $user_id );
$stmt->execute();
$stmt->bind_result($user_id, $field_id, $value);
while($stmt->fetch())
{
$data[$user_id][$field_id] = $value;
}

Categories