combining 2 mysql queries into 1 - php

i am having trouble creating a single mysql query for what i am trying to do here.
first off, i will show you the table structures and fields of the tables i am using for this particular query:
users:
- id
- name
- photo_name
- photo_ext
user_attacks:
- id
- level
user_news_feed:
- id
- id_user
- id_status
- id_attack
- id_profile
- id_wall
- the_date
user_status:
- id
- status
user_wall:
- id
- id_user
- id_poster
- post
whenever the user posts an attack, or status update, updates their profile, or posts on someones wall, it inserts the relevant data into its respective table and also inserts a new row into the user_news_feed table.
now, what i want to do is select the last 10 news feed items from the database. these news feed items need to grab relevant data from other tables as long as their value is not 0. so if the news feed is for a status update, the id_status would be the id of the status update in the user_status table, and the "status" would be the data needing to be selected via a left join. hope that makes sense.
heres my first mysql query:
$sql = mysql_query("select n.id_user, n.id_status, n.id_attack, n.id_profile, n.id_wall, n.the_date, u.id, u.name, u.photo_name, u.photo_ext, s.status
from `user_news_feed` as n
left join `users` u on (u.id = n.id_user)
left join `user_status` s on (s.id = n.id_status)
where n.id_user='".$_GET['id']."'
order by n.id desc
limit 10
");
now this works great, except for 1 problem. as you can see the user_wall table contains the id's for 2 different users. id_user is the user id the post is being made for, and id_poster is the user id of the person making that wall post. if the user makes a wall post on his/her own wall, it is inserted into the database as a status update into the user_status table instead.
so i have a conditional statement within the while loop for the first query, which has another sql query within it. here is the whole code for the while loop and second sql query:
while ($row = mysql_fetch_assoc($sql))
{
if ($row['id_wall'] != 0)
{
$sql_u = mysql_query("select u.id, u.name, u.photo_name, u.photo_ext, w.post
from `user_wall` as w
left join `users` u on (u.id = w.id_poster)
where w.id='".$row['id_wall']."'
");
while ($row_u = mysql_fetch_assoc($sql_u))
{
$row['photo_name'] = $row_u['photo_name'];
$row['photo_ext'] = $row_u['photo_ext'];
$row['id_user'] = $row_u['id'];
$row['name'] = $row_u['name'];
$content = $row_u['post'];
}
}
else
{
if ($row['id_status'] != 0)
$content = $row['status'];
else if ($row['id_attack'] != 0)
$content = '<i>Had an attack</i>';
else if ($row['id_profile'] != 0)
$content = '<i>Updated profile</i>';
}
echo '<li'.(($count == $total_count) ? ' class="last"' : '').'>';
echo '<img src="images/profile/'.$row['photo_name'].'_thumb.'.$row['photo_ext'].'" alt="" />';
echo '<div class="content">';
echo '<b>'.$row['name'].'</b>';
echo '<span>'.$content.'</span>';
echo '<small>'.date('F j, Y \a\t g:ia', $row['the_date']).'</small>';
echo '</div>';
echo '<div style="clear: both;"></div>';
echo '</li>';
}
i hope what i am trying to do here makes sense. so basically i want to have both sql queries ($sql, and $sql_u) combined into a single query so i do not have to query the database every single time when the user_news_feed item is a wall post.
any help would be greatly appreciated and i apologise if this is confusing.

SELECT n.id_user, n.id_status, n.id_attack, n.id_profile, n.id_wall, n.the_date,
u.id, u.name, u.photo_name, u.photo_ext, s.status,
w.id AS wall_user_id, w.name AS wall_user_name,
w.photo_name AS wall_user_photo_name,
w.photo_ext AS wall_user_photo_ext,
w.post
FROM user_news_feed AS n
LEFT JOIN users AS u ON (u.id = n.id_user)
LEFT JOIN user_status s ON (s.id = n.id_status)
LEFT JOIN (SELECT a.id AS id_wall, b.id, b.name, b.photo_name, b.photo_ext, a.post
FROM user_wall AS a
LEFT JOIN users AS b ON (b.id = a.id_poster)
) AS w ON w.id_wall = n.id_wall
WHERE n.id_user = ?
ORDER BY n.id desc
LIMIT 10
The '?' is a placeholder where you can provide the value of $_GET['id'].
Basically, this adds an extra outer join, to the main query (and some extra columns, which will be NULL if the news feed event is not a wall posting), but the outer join is itself the result of an outer join.

Back again ;)
Anyway, forget about merging the queries in my opinion.
What you should do instead is to do the first query, loop through all the results and store all "id_wall"s in a separate array... then rather than doing a separate query per "id_wall" you do this:
$wallids = array();
while ($row = mysql_fetch_assoc($sql))
{
$wallids[] = $row['id_wall'];
// also store the row wherever you want
}
$sql_u = mysql_query("select u.id, u.name, u.photo_name, u.photo_ext, w.post
from `user_wall` as w
left join `users` u on (u.id = w.id_poster)
where w.id IN ('".implode(',', $wallids) ."')
");
$wallids being an array with all the "id_wall"s. So now you have a total of 2 queries.

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();

Can't seem to return all values in database for displaying

I created a simple upvote system for my website, I have a little bug where it will not grab all of the ID's that I need for displaying. The issue is that I have multiple repeat values in the database for the ID of each item that the user's voted on.
I created a separate table for the votes so that I can update the arrow color when it has been voted.
Here is the code for displaying the results, it's in a foreach loop:
if(isset($_SESSION['user_session'])){
if(($row['voteLikes_topic'] == $row['topic_id']) && ($row['voteLikes_user'] == $_SESSION['user_session'])){
var_dump($row['voteLikes_topic']);
$returnValues .= '<i class="fa fa-arrow-up"></i> ';
}else{
$returnValues .= '<i class="fa fa-arrow-up"></i> ';
}
}
Here are the columns in the database for the voting results:
Here is the query:
$query = "SELECT
count(replies.reply_topic) as replyCount,
topics.topic_id,
topics.topic_subject,
topics.topic_date,
topics.topic_cat,
topics.topic_picture,
topics.topic_creator,
topics.topic_likes,
users.user_id,
users.username,
profile.profile_id,
profile.profile_pictureMain,
profile.profile_users,
savelink.saveLink_id,
savelink.saveUser_id,
savelink.link_id,
votelikes.voteLikes_user,
votelikes.voteLikes_topic,
votelikes.voteLikes_hot,
votelikes.voteLikes_cold
FROM
topics
LEFT JOIN
users
ON
topics.topic_creator = users.user_id
LEFT JOIN
replies
ON
replies.reply_topic = topics.topic_id
LEFT JOIN
profile
ON
profile.profile_users = users.user_id
LEFT JOIN
savelink
ON
savelink.link_id = topics.topic_id
LEFT JOIN
votelikes
ON
votelikes.voteLikes_topic = topics.topic_id
GROUP BY
topics.topic_id
ORDER BY
topics.topic_date DESC
LIMIT ?, ?
";
And here is what the query is displaying (for clarity)
So what is happening right now is that once it grabs the 'voteLikes_topic' value of say 66 for the 'voteLikes_user' ID of 41 then if I log in with voteLikes_user ID of 6 and it also has a value in the column of voteLikes_topic 66, it won't display the second value in the database.
Simply put: since 'voteLikes_topic' value of '66' came first with 'voteLikes_user' value of '41', then the voteLikes_topic value of 66 won't display for voteLikes_user value of 6.
I know it's a simple query problem but I am not finding it. Thanks in advance.

Query inside of another query?

I've searched and found ways to do queries inside queries, but I have not found the right answer.
What I am trying to do is to fetch comments from the table called users_profiles_comments and by doing that I will get the field of '.$member["author"].'. Then I want to show who made the comment with their rank and a display picture.
But because there is no user details stored in the comments table because its for comments, I need to create a query inside of my query result where I can have this:
"SELECT * FROM users WHERE username = $member['author'] ORDER BY `id` ASC"
The '.$member["author"].' value comes from the main query but I need it so it can find fields in my users table.
$sql = "SELECT * FROM users_profiles_comments WHERE postid = '1' ORDER BY `id` ASC";
$stm = $dbh->prepare($sql);
$stm->execute();
$users = $stm->fetchAll();
foreach ($users as $row) {
print '
<img src="'.$member["profilepic"].'" style="float:left;margin-right:10px;" width="80px" height="85pxm">
<h4>'. $row["author"] .'
<small>'. $row["date"] .'</small>
</h4>
<p>'. $row["content"] .'</p><hr> </a></li>';
}
echo '
</div>
';
I hope you understand.
Edit:
The database.
users Table
http://img855.imageshack.us/img855/4387/qyjl.png
users_profiles_comments table
http://i.imgur.com/aKRF5MN.png
But because there is no user details stored in the comments table because its for comments
This is good. You do not want to have your database information duplicated in both tables (de-normalized data) - there is no need.
Generally a foreign key is added to inverse side of the relationship. This would mean you have a user_id column within your comments table, allowing you to select the data in one query using a JOIN
SELECT comment.*, user.name AS comment_author
FROM comment
INNER JOIN user ON user.id = comment.user_id
WHERE user.id = 123
Edit - I flipped to JOIN to return comments rather than users, (as you said "to fetch comments") however both ways will work, it just depends which side of the relationship you are on.
Edit 2 To respond to your comment;
How would I be able to show this on my PHP code on the result?
you can be more specific with the above query (not use *) and render it as follows :
$sql = "
SELECT
comment.`date` as comment_date,
comment.content,
user.name AS author_name,
user.profilepic as author_pic
FROM
users_profiles_comments as comment
INNER JOIN
user ON user.id = comment.user_id
WHERE
comment.postid = 1
";
//...
$comments = $stmt->fetchAll();
foreach ($comments as $comment) {
echo '<img src="' . $comment['author_pic'] . '"/>';
echo '<h4>' . $comment['author_name'] . '<small>' . $comment['comment_date'] .'</small></h4>';
echo '<p>' . $comment['content'] . '</p>';
}
You can select member name in SQL statement. You can try this query, this query will bring all the information from two table if users.username = users_profiles_comments.author
WAY 1:
SELECT t1.*, t2.* FROM
(
SELECT * FROM users WHERE username = '.$member["author"].' ORDER BY `id` ASC
) AS t1
LEFT JOIN
(
SELECT * FROM users_profiles_comments WHERE postid = '1' ORDER BY `id` ASC
) AS t2
ON t1.username = t2.author
Finally, do your foreach to print
===================================================================
WAY 2:
You can try this also-
SELECT *,
(SELECT profilepic FROM users WHERE users.username = users_profiles_comments.author) AS profilepic
FROM users_profiles_comments WHERE postid = '1' ORDER BY `id` ASC
And print profilepic using $row['profilepic'] in your foreach loop
=====================================================================
WAY 3:
SELECT t1.*, t2.* FROM
(
SELECT * FROM comments WHERE postid = '1'
) AS t1
LEFT JOIN
(
SELECT * FROM users
) AS t2
ON t1.author = t2.username
What I understand is that you need to get information about the user from another table called user_profile_comments, based on the user id (correct me if wrong: :-)). Use a left outer JOIN to fetch data from another table, limited on user id.
See http://www.w3schools.com/sql/sql_join.asp for more info.

ORDER BY variable AFTER statement

Here is my code:
<?php
$data = mysql_query("SELECT * FROM board") or die(mysql_error());
while($info = mysql_fetch_assoc( $data ))
{
if(!empty($info['user'])){
Print "".$info['user'].""; }
else {
}
myOtherQuery($info['id']);
}
function myOtherQuery($id) {
$result3 = mysql_query("SELECT COUNT(source_user_id) FROM likes
INNER JOIN pins ON pins.id = likes.pin_id
WHERE pins.board_id='$id'");
$c = mysql_result($result3, 0); // Cumulative tally of likes for board
{
Print "$c";
}
}
?>
The first part gets a users name and board details (board as in a photo album).
the second part joins that data with another sql table that counts the number of likes that board has.
Both are displayed as a name and a score represented by a number.
By default they are ordered by the date of creation. I'd like to be able to order them by the score. However, since the score is determined in the second part of the code, I don't know how to achieve it. Is it possible?
The solution is of course to query both at once in the first place, via a LEFT JOIN against a subquery returning the count per board_id:
SELECT
board.*,
/* Your PHP code will retrieve the likes count via this alias `numlikes` as in $info['numlikes'] */
numlikes
FROM
board
LEFT JOIN (
/* Subquery returns count per board_id */
SELECT pins.board_id, COUNT(source_user_id) AS numlikes
FROM
likes
INNER JOIN pins ON pins.id = likes.pin_id
GROUP BY pins.board_id
) likes ON board.id = likes.board_id
ORDER BY numlikes
It is nearly always significantly more efficient to perform a single query rather than n queries in a loop. You should strive to do so whenever possible.
You can do it in one query
SELECT board.*, count(likes.source_user_id) as score
FROM board
INNER JOIN pins
ON pins.board_id = board.id
INNER JOIN likes
ON pins.id = likes.pin_id
ORDER BY score

Combining Multiple MySQL JOIN Queries

I have this query which echos IDs of assignments for classes which users are enrolled in.
$sql = $db->prepare("SELECT assignments.*, enrollments.course_id, enrollments.student_id
FROM assignments
LEFT JOIN enrollments
ON assignments.course_id = enrollments.course_id
LEFT JOIN completed
ON assignments.id != completed.assignment_id
WHERE enrollments.student_id = ?
ORDER BY assignments.id DESC LIMIT 10
");
$sql->execute(array($login_id));
while($row = $sql->fetch())
{
echo $row['id'];
}
What would be the best way to do yet another check where I see if the assignment has been marked as completed?
This means that it would also need to check the "completed" table and make sure there is no row where the $login_id and assignment.id are present together for any of the assignments selected.
Here's a query I have right now to find completed assignment IDs for a user logged in.
$sqlcomplete = $db->prepare("SELECT * FROM completed
INNER JOIN students ON completed.student_id = students.id
WHERE completed.student_id = ?
");
$sqlcomplete->execute(array($login_id));
while($row = $sqlcomplete->fetch(PDO::FETCH_ASSOC))
{
echo "<li>You have completed assignment with ID ".$row['assignment_id']."</li>";
}
I've tried to do a more complex JOIN but I can't seem to figure it out. I also considered simply creating an array of the IDs of the assignments which the user has completed by querying that database alone, and throwing that ID into the while check, but I feel like that is not the best or most efficient solution.
You can use a LEFT JOIN and when completed.assignment_id IS NULL then that means there was no match returned from the completed table.
SELECT assignments.*, enrollments.course_id, enrollments.student_id
FROM assignments
LEFT JOIN enrollments ON assignments.course_id = enrollments.course_id
LEFT JOIN completed ON assignments.id = completed.assignment_id
WHERE enrollments.student_id = ?
AND completed.assignment_id IS NULL
ORDER BY assignments.id DESC LIMIT 10

Categories