MySQL adding to query nullifies existing parts - php

Database:
xx_users (id, name, user_id, email, location)
xx_questions (id, question, description, user_id)
Original code:
$friends = $facebook->api('/me/friends');
$arr= $friends['data'];
$friend_ids_arr = array();
foreach($arr as $friend) {
$friend_ids_arr[] = $friend['id'];
}
$sql = "SELECT * FROM xx_questions WHERE user_id IN (" . implode(',', $friend_ids_arr) . ") OR user_id = '$user' ORDER BY time DESC";
New code:
$friends = $facebook->api('/me/friends');
$arr= $friends['data'];
$friend_ids_arr = array();
foreach($arr as $friend) {
$friend_ids_arr[] = $friend['id'];
}
$sql = "SELECT *
FROM xx_questions q JOIN xx_users u ON
q.user_id = u.user_id
WHERE q.user_id IN (implode(',', $friend_ids_arr)) OR // STEP 1
q.user_id = '$user' OR // STEP 2
u.location = (SELECT location FROM xx_users WHERE user_id = $user) // STEP 3
ORDER BY q .time DESC";
$data = mysql_query($sql) or die(mysql_error());
while ($row = mysql_fetch_assoc($data)) {
echo $row[id];
}
I am trying to select questions from xx_questions (that have been posted by users) should any of three situations arise:
Those values where the poster's user_id matches that of one of the current user's friends (i.e. the poster and the user are friends)
Those values where the poster's user_id matches that of the user (i.e. the poster is the user)
Those values where the poster's location matches that of the current user (i.e. the poster and user are currently in the same city)
I had originally achieved steps 1 and 2, but when I add step 3, two things happen:
Step 1 stops working (i.e. only posts from the current user and people in the same city are returned)
The $row[id] echoed in the while loop is that of xx_users, not xx_questions. I've tried $row[q.id] but to no avail.
Any idea what's gone wrong?

In your seconds query your implode statement isnt outside the string.
"WHERE q.user_id IN (implode(',', $friend_ids_arr)) OR" // STEP 1
While php can add variable to a double quoted string ($x = "test $test test" $test will be changed to the value of $test) it can't escape functions. So this would return in
"WHERE q.user_id IN (implode(',', Array)) OR" // STEP 1
You should use:
"WHERE q.user_id IN (".implode(',', $friend_ids_arr).") OR" // STEP 1
And in the end you are using SELECT * and if both tables have an Id column, only one is returned. Its better to use SELECT q.* to get only the questions row or perhaps SELECT q.*, u.location to get the entire question row and the location from the user table.
I also checked your query and it should do what you want
$sql = "SELECT q.*, u.location
FROM xx_questions q
JOIN xx_users u
ON q.user_id = u.user_id
WHERE q.user_id IN (".implode(',', $friend_ids_arr).") OR // STEP 1
q.user_id = $user OR // STEP 2
u.location = (SELECT location FROM xx_users WHERE user_id = $user) // STEP 3
ORDER BY q.time DESC";
The only thing I changed is the implode and remove the '' around $user. That is incase the $user isnt actualy an integer but a string containing a number, but has some extra spaces appended (eg '1 ' instead of '1');

Related

Showing two different values depending on SESSION value in INNER JOIN

I have two different tables, one named users, and another named transactions. Transactions contains wallet1, wallet2, amount. Users contains user details such as firstname, lastname, and wallet. I am trying to display the corresponding first name and last name, depending on whether or not the SESSION_wallet is equal to wallet1 or wallet2 within transactions. I tried searching for a while, and came up with a solution for showing the correct display name for the first and last name making the transfer, however, I am trying to make it display the correct value for "Transfer to:"
Here is some of my code to get a better understanding of what I mean:
MySQLi Query:
$result2 = mysqli_query($link, "SELECT * FROM transactions INNER JOIN users ON transactions.wallet1 = users.wallet WHERE transactions.wallet1 = '" . $_SESSION["wallet"] . "' OR transactions.wallet2 = '" . $_SESSION["wallet"] . "' Order by transactions.id DESC LIMIT 5 ");
PHP Code:
<?php
if(mysqli_num_rows($result2) > 0)
{
while($row = mysqli_fetch_array($result2))
{
?>
The table that needs to display the transfer from, and transfer to:
<?php
if ($_SESSION["wallet"] == $row["wallet1"]) {
echo "<td>Transfer to ".$row["firstname"]." ".$row["lastname"]."</td>";
}
else if ($_SESSION["wallet"] == $row["wallet2"]) {
echo "<td>Transfer from ".$row["firstname"]." ".$row["lastname"]."</td>";
}
?>
Right now my tables are only showing the first and last name of the user that made the Transfer, however, I need it to display the first and last name of the user that the transaction is made to as well. The else if code is working correct, but the first part is not showing the corresponding value.
You will need to JOIN your transactions table to your users table twice, once to get each users name. Then to avoid duplicate column names overwriting the results in the output array, you will need to use column aliases. Something like this should work:
$result2 = mysqli_query($link, "SELECT t.*,
u1.firstname AS w1_firstname,
u1.lastname AS w1_lastname,
u2.firstname AS w2_firstname,
u2.lastname AS w2_lastname
FROM transactions t
INNER JOIN users u1 ON t.wallet1 = u1.wallet
INNER JOIN users u2 ON t.wallet2 = u2.wallet
WHERE t.wallet1 = '{$_SESSION["wallet"]}'
OR t.wallet2 = '{$_SESSION["wallet"]}'
ORDER BY t.id DESC
LIMIT 5 ");
Then you can access each user's names as $row['w1_firstname'] etc.:
if ($_SESSION["wallet"] == $row["wallet1"]) {
echo "<td>Transfer to ".$row["w2_firstname"]." ".$row["w2_lastname"]."</td>";
}
else if ($_SESSION["wallet"] == $row["wallet2"]) {
echo "<td>Transfer from ".$row["w1_firstname"]." ".$row["w1_lastname"]."</td>";
}
Note that ideally you should use a prepared query for this, for example:
$stmt = $link->prepare("SELECT t.*,
u1.firstname AS w1_firstname,
u1.lastname AS w1_lastname,
u2.firstname AS w2_firstname,
u2.lastname AS w2_lastname
FROM transactions t
INNER JOIN users u1 ON t.wallet1 = u1.wallet
INNER JOIN users u2 ON t.wallet2 = u2.wallet
WHERE t.wallet1 = ?
OR t.wallet2 = ?
ORDER BY t.id DESC
LIMIT 5");
$stmt->bind_param('ss', $_SESSION["wallet"], $_SESSION["wallet"]);
$stmt->execute();
$result2 = $stmt->get_result();

How can control bind param variable in PHP

I'm selecting a list of exam IDs from my database, and then using a random one to select from a list of questions. If there are no matching questions, I want to pick another random ID and try again. Here is the code I have now; it works but will not return any questions for some of the exams.
$query = $connect->prepare("SELECT * FROM exam WHERE level=? AND flag=?");
$query->bind_param("si",$level,$flag);
$query->execute();
$result = $query->get_result();
$resultCount= $result->num_rows;
if($resultCount>0)
{
while($row=$result->fetch_assoc())
{
$list[]=$row['id'];
}
$indexRand=array_rand($list, 1);
$query->close();
}
$query2 = $connect->prepare("SELECT * FROM questions WHERE exam_id=?");
$query2->bind_param('i', $list[$indexRand]);
$query2->execute();
$question_result = $query2->get_result();
$resultCount2= $question_result->num_rows;
if($resultCount2>0)
{
while($questions_rows=$question_result->fetch_assoc())
{
$list2[]=$questions_rows;
}
}
If I understand correctly, you can do this in a single database query.
SELECT q.*
FROM questions q
LEFT JOIN (
SELECT e.id
FROM exams e
LEFT JOIN questions q ON (e.id = q.exam_id)
WHERE level = ? AND flag = ? AND q.id IS NOT NULL
ORDER BY RAND()
LIMIT 1
) i ON (q.exam_id = i.id)
WHERE i.id = q.exam_id
You join in a subquery as a table in your query. The subquery selects a single random item from the exams table, and itself joins in the questions table, to make sure that some questions exist. Demo is here: http://sqlfiddle.com/#!9/b394af/1

From PHP how to efficiently execute this search in SQL Server?

I have a database structure something like the following:
Table A: PersonId, GroupId
Table B: GroupId, ParentGroupId
Given a PersonId, I want to find the Ids of all people in parent groups of that person's group.
First I select the ParentGroupId for the given PersonId, by joining with B. Then I do a while loop, selecting and recording the PersonId from A based on the GroupId returned in the previous search, and continue the loop by obtaining the next ParentGroupId from B.
Is this an efficient way to do this search, or is there an option that does not involve a while to "bubble up" in this manner?
(this is a simplified version of the actual scenario, changing the schema is not an option)
$sql = 'SELECT ParentGroupID FROM A WHERE PersonId = ' . $id;
$result = $db->query($sql);
$row = $db->fetch_array($result);
$parent_group = $row['ParentGroupId'];
if(!is_null($parent_group)) {
$parent_ids = array();
while($parent_group > 0) {
//is there a way to do this where I retrieve all managers <= lvl 6 at once, so I don't have to loop in order to 'tier up'?
$sql = 'SELECT ParentGroupID, PersonID
FROM B
INNER JOIN A on ParentGroupID = A.GroupID
WHERE ParentGroupID = ' . $parent_group;
$result = $db->query($sql);
$row = $db->fetch_array($result);
$parent_group = $row['ParentGroupID'];
$parent_ids[] = $row['PersonID'];
}
}
Combining your two queries into one would be more efficient:
$sql = 'SELECT ParentGroupID, PersonID
FROM B
INNER JOIN A on ParentGroupID = A.GroupID
WHERE ParentGroupID IN (
SELECT ParentGroupID FROM A WHERE ParentGroupID > 0
AND PersonId = ' . $id .')' ;

select * that match user postcode in mysql table?

I'm trying to display a list of users who live locally to the user who is logged in. so if the logged in session user has a postcode of 'm3 4' and 5 other users have a postcode beginning with 'm3 4' then these users will be shown to the user.
My table is laid out like this:
id | user_id | user_postcode
1 2 M3 4
2 3 SM2 7
3 4 M3 4
so in this scenario user 2 will be shown to user 4 who is logged in because their post codes match.
I'm trying to do this in mysql and it works when i put the postcode in manually like so:
AND ptb_stats.user_postcode='M3 4'
but I'm trying to make it user session specific, so if the logged in user / $_SESSION[user_id'] has the same post code as other users.
i'm trying to do it this way, but it's showing all the users without postcodes where as it should be showing the users that have matching postcodes, shouldn't it?
function get_local_users() {
global $connection;
global $_SESSION;
$query = "
SELECT *
From ptb_stats, ptb_users
WHERE ptb_stats.user_id=ptb_users.id
AND ptb_stats.user_postcode='".$_SESSION['user_postcode']."'";
$local_set = mysql_query($query, $connection);
confirm_query($local_set);
return $local_set;
}
With this answer, I assume that the $_SESSION['user_postcode'] is filled from some type of input box and the value is a valid zipCode (like "M3 4").
You can use preg_match to split the zipcode from the number and try to select zipcodes form the db. Take a look at this example:
$matches = array();
$zipCode = preg_match('/^([a-z0-9]+)/i', $_SESSION['user_postcode'], $matches);
The zipcode is now in the $matches variable at the second place ($matches[1]).
Now use this value to create a query and check if it is the same as others..
$query = "SELECT *
From ptb_stats, ptb_users
WHERE ptb_stats.user_id=ptb_users.id
AND ptb_stats.user_postcode REGEX '^" . $matches[1] . "'";
According to what you describe, I will go onto something like that.
I used prepared statement from PDO to handle the query.
function get_local_users() {
// given that it was created like this: $connection = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 'username', 'password');
global $connection;
$stmt = $connection->prepare(
"SELECT ptb_stats.*
FROM ptb_stats, ptb_users
WHERE ptb_stats.user_id = ptb_users.id
AND ptb_stats.user_id != ?
AND ptb_stats.user_postcode = (
SELECT user_postcode
FROM ptb_stats
WHERE user_id = ?
)");
$stmt->execute(array($_SESSION['user_id'], $_SESSION['user_id']));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $rows;
}
If the user id is stored in $_SESSION['user_id'], you can get the local users with
select u.id, u.name
from ptb_users u
join ptb_stats s1 on s1.user_id = $_SESSION['user_id']
join ptb_stats s2 on s2.user_id = u.id and s2.user_postcode = s1.user_postcode
where u.id <> $_SESSION['user_id']
SQLFiddle
If you have the postcode in $_SESSION['user_postcode'], it is even easier
select u.id, u.name
from ptb_users u
join ptb_stats s on s.user_id = u.id
and s.user_postcode = $_SESSION['user_postcode']
where u.id <> $_SESSION['user_id']
SQLFiddle

MySQL query involving three stages and two tables

Database:
xx_users (id, name, user_id, email, location)
xx_questions (id, question, description, user_id)
Code:
$friends = $facebook->api('/me/friends');
$arr= $friends['data'];
$friend_ids_arr = array();
foreach($arr as $friend) {
$friend_ids_arr[] = $friend['id'];
}
$sql = "SELECT (SELECT * FROM xx_questions WHERE user_id IN (" . implode(',', $friend_ids_arr) . ") OR user_id = '$user' ORDER BY time DESC) (SELECT * from questions INNER JOIN users ON users.user_id = questions.user_id WHERE users.location = 'Cambridge, Cambridgeshire')";
$data = mysql_query($sql) or die(mysql_error());
while ($row = mysql_fetch_assoc($data)) {
// echo values from what's been selected
}
I am trying to select questions from xx_questions (that have been posted by users) should any of three situations arise:
Those values where the poster's user_id matches that of one of the current user's friends (i.e. the poster and the user are friends)
Those values where the poster's user_id matches that of the user (i.e. the poster is the user)
Those values where the poster's location matches that of the current user (i.e. the poster and user are currently in the same city)
I have managed to make 1 and 2 work, but the problems arise when I try to introduce 3, so my two questions are:
What is the correct MySQL for 3?
How is that integrated with 1 and 2?
Thanks in advance, let me know if you need more information.
To get the information that you want, you need to join xx_questions to xx_users. Something like the following returns questions that match any one of the three conditions:
select *
from xx_questions q join
xx_users u
on q.user_id = u.user_id
where q.user_id in (<implode(',', $friend_ids_arr)>) or
q.user_id = '$user' or
u.location = (select location from xx_users where USER_ID = $user)
I have written this more like SQL than as a string, so it is easier to read.

Categories