PHP Chat System MySQL Database Order by datesent sql - php

I have a php chat system that I am working on. I have an issue where the query statement to grab the message is selecting them, grouping them by the two users that it was sent to and from, and then outputs the html.
Right now it only is outputting from the beginning of the first chat id to the last id when a user goes to select a chat.
I would like to sort it by the most recent chat on top because as of the moment, it is going from top to bottom from the first user that has sent a message to the last user that has sent a message. For example, if "user 1" hasn't sent a message for a while, that user will always be the first one to be outputted by the query statement, even though "user 6" has sent 8 new chats, "user 6" will still be the last one on the list to select from, this should be the opposite and the newest chat group should appear on top. I just don't know how to do that with my SQL query.
My current query statement is:
$queryall = "SELECT DISTINCT mfrom FROM chat WHERE mto='user2'";
I know it's coded wrong for what I want, and i've tried a lot of different things, can't seem to get it to do what I want. I've tried order by datesent and grouping them, but couldn't get anything to work. I believe DISTINCT is not allowing me to do it based of syntax things I am not familiar with.
mysql database row
User2 chat view

You just need a simple ORDER BY:
$queryall = "SELECT DISTINCT mfrom FROM chat WHERE mto='user2' ORDER BY datesent DESC";
This orders your result set by column datesent in DESC (descending) order (newest to oldest when used on date/datetime column).
When your users have multiple messages, you may want to use GROUP BY instead of DISTINCT
$queryall = "SELECT mfrom FROM chat WHERE mto='user2' GROUP BY mfrom ORDER BY MAX(datesent) DESC";

Related

MySQL - Select last rows of LEFT JOIN column based on the fields

I am building a mobile chat web application and I have stuck in a problem. Coming straight to the issue, I have a screen which displays messages list from all users (like WhatsApp). I want to display the last message sent or received between the users in the list (as in the screenshot below). My current query extracts the message from the 1st row for all users right now. That's not what I want.
Little more brief details of what is happening
As you can see in messages table, the fields msg_from and msg_to represents the sender and the receiver respectively. In my data, the messages are transferred between user 1 & 8 and user 1 & 11. For user 1 & 8 the last record fetched should be record 9 which has msg_message Are you there? and similarly, for user 1 & 11 the last record to be fetched would be record 10 which has msg_message Would you like to join?. But currently, for all users the record getting fetched is the 1st record with message How are you?. What changes should my query have to get the desired result? Please have a look at the fiddle below.
Fiddle Here: DB Fiddle
I learned a lot from researching in order to solve this. When grouping, groupBy will take the first row of non-grouped columns (suck as msg_message), so we may order it when joining with the help of a subquery, just like this:
SELECT swp_by, swp_to, msg_from, msg_to, mem_fname, mem_lname, mem_last_activity, msg_message, GREATEST(MAX(msg_time), swipes.swp_date) as msgdate, COUNT(msg_id) as msgcnt FROM swipes
LEFT JOIN
(
SELECT * FROM messages order by msg_time desc -- this is the magic, we use this subquery to order before grouping
)
messages ON
((
messages.msg_from = swipes.swp_by
AND messages.msg_to = swipes.swp_to)
OR (messages.msg_from = swipes.swp_to
AND messages.msg_to = swipes.swp_by
))
solution is in your fiddle: https://www.db-fiddle.com/f/xnh4jiUb8rDLFHpL2gWHrM/5
I think I got expected output

Select data from one table, arrange by a sum of data from another

A client is looking for a points system to be implemented on her website, I'm struggling to display the users based upon the amount of points collected, I hope somebody may be able to help me out here and point me in the right direction to getting this code to work properly.
I am selecting all data from ap_users and in the code I am also trying to select all data from ap_points although I do not require all the data from either tables, to be specific I only require:
ap_users:
user_id
first_name
last_name
display_img
hub_access
ap_points:
user_id
points_added
I thought that selecting ALL data may be the easiest route, will let you decide.
I am trying to select and display all users where hub_access = '1' and order by the total points_added by highest first. Points are added separately by rows and need to be added up (which is why I have the sum function).
$sql = "SELECT * FROM `ap_users`, `ap_points` WHERE `hub_access` = '1' ORDER BY sum(points_added) DESC";
I also tried configuring it to be specific tables like:
ap_users.hub_access and ORDER BY sum(ap_points.points_added) but these did not work either.
This current code is either showing no results or a single result with no errors displaying? I'm not sure whether I may need to use some kind of Group By function to connect the user_ids from both tables ?
SUM is an aggregating function. You should be grouping by user_id if you want the sum for each user_id.
Something like
SELECT *, sum(points_added) as sum_points FROM app_users
JOIN app_points ON app_users.user_id = app_points.user_id
WHERE `hub_access` = '1'
GROUP BY app_users.user_id
ORDER BY sum_points;
I have not tested that query, but that should give you an idea of the solution.

Reversing the display of mysql_fetch_array on html page

I have a discussion type page, where users can enter their replies on different subjects. For this, I have a table name replies with fields replyID, topicID, userID, replybody, time
On the page, where I display the result, I use this php command:
$run = mysql_query("SELECT * FROM replies WHERE topicID = $topicnumber ORDER BY time DESC Limit 5");
while($data= mysql_fetch_array($run)){
// do formatting and display data
}
as you can see, the page initially displays only 5 replies on a topic (after which, if interested, user clicks to visit the page where all replies are displayed).
the thing is, the above code displays correctly the recent 5 replies, but I want to change the order of its display. It shows the recent most reply on top, and older replies are displayed as we go down, but I want to change this order, showing the recent most reply on bottom.
I think I'm missing a very simple point here, since most of the website have this feature where recent most comments go down, but hey, "no question is small".
I don't think that's very easy in sql.
I would just load the results in an array and use array_reverse() to reverse the order. Then you can loop through the array and display the values like you do now.
Otherwise I think you need to do 2 queries, one to get the total amount and then one to limit your result set to the last x items.
Use a nested query to reverse the order in SQL:
SELECT *
FROM (
SELECT * FROM replies WHERE topicID = $topicnumber ORDER BY time DESC Limit 5
) AS a
ORDER BY time ASC
The inner query will return your most recent 5 records, while the outer query will reverse the sort order of those 5.

SQL JOINS retrieving and displaying twice

I am joining 2 tables and trying to display the results. Only problem is every result is duplicated. I have 2 tables, messages and follow. Messages are what a certain user inputs, and I want it to display only to the people that follow that certain user.
Messages | Follow
-id -id
-message -mem1 (logged in user)
-userid -mem2 (followed user)
-created
$display ="";
$sql = mysql_query("
SELECT * FROM messages AS me
JOIN follow AS fl
ON me.userid = fl.mem2
WHERE fl.mem1 = $id (logged in user)
ORDER BY me.created
DESC LIMIT 10
") or die(mysql_error());
while($row=mysql_fetch_array($query)){
$msgid = $row["id"];
$message = $row["message"];
$userid = $row["userid"];
$created = $row["created"];
$display .="<?php echo $userid; ?> : <?php echo $message; ?><br />
<?php echo $created; ?>";
}
In the database there are no duplicates, just on the retrieve. Thanks for the input!
Edited: Display Code
You're getting "double" results, most likely because the query results in something different then you expect.
If I understand your table-structure correctly; you have a one-to-many relation from messages to followers.
In your query, however, you fetch combinations of messages and followers. Each line will consist of a unique combination of message<>follower.
In short; when a single message has two followers, you'll get two rows in the result with the same message; but a different follower entry.
If you want to show each message once; and then list all followers per message you can either use group-by functions (e.g group_concat) and group-by on message entries. The other possibility is to fetch the followers in a separate query once you've retrieved the message row, and then print the results from that query as the followers for that message.
If you're simply trying to get the number of followers; you can use a group-by on the UID of your message table and add a count on the UID or user ID of the follower table. (Do not that with group-by, the select * from shouldn't be used; but separate columns can.)
There's really only a few things that could cause the records to duplicate - try breaking down the query into basic components to see if there are more than one record:
SELECT * FROM follow WHERE mem1 = [id];
SELECT * FROM messages WHERE userid = [mem2 from previous result];
If either of the previous statements return more than one record, than the problem lies there. Other than that, I'd look at the PHP code to see if you're doing something there.
As for the query itself, I have a few recommendations:
Place the table with the filter first - the sooner you can narrow the results the better.
Specify a field list instead of using '*' - this will be a tiny bit more efficient, and clarify what you're after. Also, it will give 'DISTINCT' a fighting chance to work...
Here's an example:
SELECT DISTINCT me.id, me.message, me.userid, me.created
FROM follow AS fl
INNER JOIN messages AS me ON me.userid = fl.mem2
WHERE fl.mem1 = :logged_in_user
ORDER BY me.created
DESC LIMIT 10
If you are sure that there are no duplicates and the problem is in the query (you can check that by executing it from your database's interface), you can try two things:
Use the follow table as the leading one:
SELECT messages.*
FROM follow
JOIN messages ON follow.mem2=messages.userid
WHERE follow.mem1=$id
ORDER BY messages.created DESC
LIMIT 0,10;
Use a subquery:
SELECT *
FROM messages
WHERE userid IN(
SELECT DISCTINCT(mem2)
FROM follow
WHERE mem1=$id
)
ORDER BY created DESC
LIMIT 0,10;

PM system - previewing previous PMs

I am working on a PM system where I'd like to have the previous sent PMs for one conversation, listed above the last received PM. But my question is: how do I go about setting up such a table in a database? I toyed for a while about using an id for each specific conversation, but what would the source for that id be? I can't use auto increment (it seems), because I'm using it for the primary "id" column.
Or maybe there's a completely different way I can experiment with the already available columns (id, from, to, subject, message, sent, read, deleted); but how? Please help a lost man out.
You could add a origin_id column to your table that contains the id of the root/original message, or NULL if it's a new discussion (root).
Then you can get the root messages by filtering those than have origin_id = NULL and then group by origin_id to get the message thread.
Okay, so I have got it partly solved...
I used another table containing the one column which holds the subject of the PM. I also have a new column in the regular "pms" table that holds the same ID to be able to join the tables together.
However, when I select all the PMs to show them in the inbox, I have not found a way to group the conversations in order by if they're read or not. I'm currently using this SQL query:
SELECT *
FROM `pms`
JOIN `pm_conversations` ON (pms.ConvID = pm_conversations.ID)
WHERE pms.To='username'
GROUP BY pm_conversations.ID
ORDER BY pms.ID
I came up with this:
SELECT MAX(pms.ID) as pmIDS,
pms.*,
pm_conversations.*
FROM `pms`
JOIN `pm_conversations` ON (pms.ConvID = pm_conversations.ID)
WHERE `To`='".$UserActive."'
GROUP BY pm_conversations.ID
ORDER BY pmIDS DESC

Categories