Select distinct from the table and sort by date - php

I am trying to built the messaging system and I want to select distinct value from the table and sort by date and also return the read and unread status. My table structure is as id, from_user_id, to_user_id, message, datetime, read . Suppose dummy data are as follows:
id | from_user_id | to_user_id | message | datetime | read
1 | 20 | 50 | hi | 2016-3-13 06:05:30 | 1
2 | 20 | 50 | hey | 2016-3-13 06:15:30 | 0
3 | 30 | 50 | hi | 2016-3-14 06:05:30 | 0
Now I want to select distinct from_user_id and sort by date so that I can display the name of the user who is chating recently on top of list and also return the read status 1, if any of the rows with from_user_id has read status 0, then I want to return read as 0 so that I can differentiate whose user message has unread message.
Suppose, from_user_id has read status as 0 in one rows then I want to return read status as 0 when returning distinct value of from_user_id. How can I do it. I tried as follows but won't work in my case. It returns distinct value but not sorted by date and also read status in not returning what I expect.
select distinct(`from_user_id`),register.name FROM message INNER JOIN register ON register.id = message.from_user_id where message.to_user_id = $user_id GROUP BY message.id ORDER BY MAX(datetime) ASC

Try the following query:
SELECT
message.from_user_id,
register.name,
message.read
FROM message
INNER JOIN
(
SELECT
from_user_id,
MAX(datetime) max_datetime
FROM message
WHERE to_user_id = 50
GROUP BY from_user_id ) t
ON t.from_user_id = message.from_user_id AND t.max_datetime = message.datetime
INNER JOIN register ON register.id = message.from_user_id
WHERE message.to_user_id = 50
ORDER BY message.datetime ASC
UPDATED SQL FIDDLE
Sample Input:
id | from_user_id | to_user_id | message | datetime | read
1 | 20 | 50 | hi | 2016-3-13 06:05:30 | 1
2 | 20 | 50 | hey | 2016-3-13 06:15:30 | 0
3 | 30 | 50 | hi | 2016-3-14 06:05:30 | 0
Output:
from_user_id name read
20 User20 0
30 User30 0
Note: This query might give you multiple row for the same from_user_id if there exists multiple entries in your message table having the same datetime and same to_user_id.

checkout this query :
SELECT DISTINCT(form_user_id),MIN(read) FROM table_name ORDER BY datetime ASC

Related

how to write a INNER JOIN on a subquery in PHP

I've a following query to select data from a table (chat) in a chat system:
SELECT * FROM (SELECT * FROM chat WHERE id_chat = '$chat_id' ORDER BY id DESC LIMIT 10) S
WHERE id_chat = '$chat_id'
ORDER BY id ASC
LIMIT 10
In this table (chat), there is a user column where it is the user id that sent the message.
I would, from the id_from of the user who sent the message, get back data that user (users table).
Table chat:
id | id_chat | id_from | id_to | message | date
1 | 23 | 1 | 2 | hello! | 01-12-2016
Table users:
id | name | photo
1 | John | pic.png
2 | Nick | hey.jpg
What better way to do it using the above query? LEFT JOIN? INNER JOIN? And, how do?

Php/mysql ranking system - Placement inside a result set ordered by column

In a blog-like website, all the users can "star" a news (= bookmark it, mark it as "favourite").
I have a mysql table for stats.
table_news_stats
id_news
total_stars (int) //Total number of users who starred this news
placement (int)
The placement field is intuitive: if you order all the news by the total_stars field you get each news placement. So, the news with most stars will be number 1, and so on.
So, suppose I have 700 records in my table_news_stats, and for each one I have the id and the total_stars count, how can I update the placement field automatically for each record? Which query is faster/better?
Example of the table_news_stats content:
First record (A):
1-3654-?
Second record (B):
2-2456-?
Third record (C):
3-8654-?
If you order the record by stars count:
the sequence of records is C - A - B
So... the result will be:
First record (A):
1-3654-2
Second record (B):
2-2456-3
Third record (C):
3-8654-1
Clarification:
why would I ever need the placement field at all?
It's pretty simple... the placement field will be populated by a cronjob the first day of every month. Basically it will provide a 'snapshot' of the rank of each news in terms of popularity (as it was at the beginning of the current month). As a consequence, thanks to the placement field, I will have the following information:
"The 1st day of this month the 'top starred' news list was like this:
1- News C
2- NewsA
3- News B "
Then, with a query "SELECT * FROM table_news_stats ORDER BY total_stars DESC" I can obtain the new ranking (in real-time).
As a consequence, I will have the following information:
"At the time the page is loaded, the 'top starred' news list is like this:
1- News A
2- News C
3- News B "
Finally, by comparing the two rankings, I obtain the last piece of information:
"News A has gained a position" +1
"News C has lost a position" -1
"News B has no change in position" +0
If there is a better way of doing this, let me know.
I guess you don't need to update the table just:
SELECT *
FROM table_news_stats
ORDER BY total_stars DESC
But if you want to know the place of each one you can:
SELECT *, IF(#idx IS NULL,#idx:= 1,#idx:= #idx+1)
FROM table_news_stats
ORDER BY total_stars DESC
And if you still need to update something like:
UPDATE table_news_stats
SET placement = FIND_IN_SET(id_news,(SELECT GROUP_CONCAT(t.id_news) FROM (SELECT id_news
FROM table_news_stats
ORDER BY total_stars DESC) t ))
SQLFiddle
Consider the following
mysql> select * from test ;
+------+-------------+-----------+
| id | total_stars | placement |
+------+-------------+-----------+
| 1 | 3 | 0 |
| 2 | 6 | 0 |
| 3 | 7 | 0 |
| 4 | 2 | 0 |
| 5 | 9 | 0 |
| 6 | 2 | 0 |
| 7 | 1 | 0 |
+------+-------------+-----------+
Now using the following you can update the placement as
update test t1 join
(
select *,
#rn:= if(#prev = total_stars,#rn,#rn+1) as rank ,
#prev:= total_stars
from test,(select #rn:=0,#prev:=0)r
order by total_stars desc
)t2
on t2.id = t1.id
set t1.placement = t2.rank ;
mysql> select * from test order by placement ;
+------+-------------+-----------+
| id | total_stars | placement |
+------+-------------+-----------+
| 5 | 9 | 1 |
| 3 | 7 | 2 |
| 2 | 6 | 3 |
| 1 | 3 | 4 |
| 4 | 2 | 5 |
| 6 | 2 | 5 |
| 7 | 1 | 6 |
+------+-------------+-----------+
Note that in case of tie will have the same placement.

Group by more combinations

I have the following table to represent a conversation between users.
message | user1 | user2 | unixtime
msg1 | 14 | 21 | -
msg2 | 21 | 12 | -
msg1 | 14 | 18 | -
msg3 | 14 | 21 | -
msg2 | 18 | 14 | -
msg4 | 21 | 12 | -
I want to show a list of a a specific user's conversations by selecting all messages with current_user = user1 or user2.
So far this SELECT will select the correct messages but the grouping doesn't work correctly.
SELECT * FROM (
SELECT * FROM messages
WHERE user1 = '".$_SESSION['login']['ID']."'
OR user2 = '".$_SESSION['login']['ID']."'
ORDER BY unixtime DESC
) as list
GROUP BY user1, user2
ORDER BY unixtime DESC
It will only group the messages with the same user1 and user2 and not if the two users are reversed.
Is this done by a more advanced grouping or should I join the table in some way?
It is unclear what you want the query to do, because you are using group by and select *. The select * returns indeterminate values from matching rows. I don't know why anyone would want to do this. You can read about this in the documentation.
If you want to counts the messages:
SELECT least(user1, user2), greatest(user1, user2), count(*)
FROM messages
WHERE '".$_SESSION['login']['ID']."' in (user1, user2)
GROUP BY least(user1, user2), greatest(user1, user2)

MySQL with selecting distinct row

Hello there, I have a schema like this, table name feeds
Where msg_id is unique and its the primary key of the table
| msg_id |commented|
| 1 | 10 |
| 2 | 10 |
| 3 | 10 |
| 4 | 21 |
I want to build a query that would select the last two rows
The output should go like this
| msg_id |commented|
| 3 | 10 |
| 4 | 21 |
In short the query should return the rows with msg_id which have a distinct commented value
Group by the column ment to be unique and select the highest id for every group
select max(msg_id) as msg_id, commented
from your_table
group by commented
Try this -
select max(msg_id), commented from your_table group by commented
SELECT * FROM feeds GROUP BY commented

Retrieve records that have been timestamp updated since last accessed / read

The following is a simplified version of the problem I'm trying to solve. I just can't figure it out.
I have two tables:
Table 1: messages
id | timestamp | message
------------------------
1 | 2014-01-20 09:00:00 | Hello I'm a message
2 | 2014-01-20 09:00:00 | Second message for you
3 | 2014-01-27 11:00:00 | This message has been updated
4 | 2014-01-28 13:45:00 | Last message for now
and
Table 2: reads
id | message_id | user_id | last_read_timestamp
------------------------
1 | 1 | 1 | 2014-01-20 09:10:00
1 | 2 | 1 | 2014-01-20 09:15:00
1 | 3 | 1 | 2014-01-24 19:25:00
1 | 1 | 2 | 2014-01-28 13:45:00
id in Table a relates to message_id in Table 2.
The messages in Table A are updated frequently and the timestamp is updated. So I need to run a query that will return the Table A id of any messages that have been updated SINCE they were read for a particular user_id, for that the user hasn't read (and therefore no corresponding entry appears in Table 2.
So for user_id = 1, message_id 3 has been updated since being read, and message_id 4 is a new message. The result for user_id = 1 would be:
Unread
------
3
4
I've tried a LEFT JOIN between the tables but just can't seem to get the correct result.
If you just need to retrieve messages unread for a single user, you could use something like this:
SELECT messages.id AS unread
FROM
messages LEFT JOIN (SELECT message_id, MAX(last_read_timestamp) as last_rd
FROM reads
WHERE user_id = 1
GROUP BY message_id) lr
ON messages.id = lr.message_id
AND messages.timestamp < lr.last_rd
WHERE
lr.message_id IS NULL
The Table A id of any messages that have been updated SINCE they were read for a particular user_id; will give below query.(not tested)
select * from table1
left join table2 on (table1.id=table2.message_id && table1.timestamp>table2.last_read_timestamp)
where table2.user_id=1;

Categories