I'm developing messaging system and have a trouble writing down the query(MYSQL).
In the nutshell query should return an array of one last messages for each conversation user to user.
CREATE TABLE 'tbl_message'('id' int(10) AUTO_INCREMENT,
'from_user' int(10),
'to_user' int(10),
'reply_to' int(10),
'subject',
'body' text,
'status' tinyint(3),
'deleted_from_sender' tinyint(3),
'deleted_from_recipient' tinyint(3)',
'created_on' datetime,
I wrote query:
SELECT * FROM tbl_message s1 WHERE s1.created_on IN (
SELECT MAX(s2.created_on)
FROM tbl_message s2 WHERE s1.from_user=".$userID." OR s2.to_user=".$userID."
GROUP BY s2.from_user, s2.to_user)
AND status = 0";
That works fine but give me 2 last messages from from_user and to_user, instead of 1 last message from both. That because of GROUP BY of course, now the question is how I could find the max created_on(actually mysql datestamp) in subquery? Or any other solution will be appreciated.
Appreciate any help or advice.
After 2 days digging stackoverflow and mysql manuals hope for help from DB prof:)
UPD:
some data for example
+----+-------+--------+--------+---------------------+--------+---------+
| id | from_user | to_user | subject | created_on | status |
+----+-------+--------+--------+---------------------+--------+---------+
| 1 | 68 | 128 | somesubject1 | 2013-07-01 21:31:29 | 0 |
+----+-------+--------+--------+---------------------+--------+---------+
| 2 | 128 | 68 | somesubject2 | 2013-07-01 21:41:29 | 0 |
+----+-------+--------+--------+---------------------+--------+---------+
| 3 | 128 | 68 | somesubject3 | 2013-07-01 21:51:29 | 0 |
+----+-------+--------+--------+---------------------+--------+---------+
| 4 | 68 | 226 | somesubject4 | 2013-07-01 22:01:29 | 0 |
+----+-------+--------+--------+---------------------+--------+---------+
output of query
| 3 | 128 | 68 | somesubject3 | 2013-07-01 21:51:29 | 0 |
+----+-------+--------+--------+---------------------+--------+---------+
| 4 | 68 | 226 | somesubject4 | 2013-07-01 22:01:29 | 0 |
You could use this query to get the maximum created_on date between two users:
SELECT
LEAST(from_user, to_user) user1,
GREATEST(from_user, to_user) user2,
MAX(created_on) max_created_on
FROM
tbl_message
GROUP BY
LEAST(from_user, to_user),
GREATEST(from_user, to_user)
and the query you are looking for is probably this:
SELECT t.*
FROM
tbl_message t INNER JOIN (
SELECT
LEAST(from_user, to_user) user1,
GREATEST(from_user, to_user) user2,
MAX(created_on) max_created_on
FROM
tbl_message
WHERE from_user=68 or to_user=68
GROUP BY
LEAST(from_user, to_user),
GREATEST(from_user, to_user)) M
on t.created_on = M.max_created_on
AND LEAST(t.from_user, t.to_user)=user1
AND GREATEST(t.from_user, t.to_user)=user2
See fiddle with both queries here.
Related
Having a hard time wrapping my mind around what seems should be a simply query.
So let's say we have a table that keeps track of amount of widgets/balloons in each store by date. How would you get a list of stores and their latest widget/balloons count?
i.e.
mysql> SELECT * FROM inventory;
+----+------------+-------+---------+---------+
| id | invDate | store | widgets | balloons|
+----+------------+-------+---------+---------+
| 1 | 2011-01-01 | 3 | 50 | 35 |
| 2 | 2011-01-04 | 2 | 50 | 35 |
| 3 | 2013-07-04 | 3 | 12 | 78 |
| 4 | 2020-07-04 | 2 | 47 | 18 |
| 5 | 2020-08-06 | 2 | 16 | NULL |
+----+------------+-------+---------+---------+
5 rows in set (0.00 sec)
Would like the result table to list all stores and their latest inventory of widgets/baloons
store, latest widgets, latest balloons
+-------+-----------+---------+
| store | widgets | baloons |
+-------+-----------+---------+
| 2 | 16 | NULL |
| 3 | 12 | 78 |
+-------+-----------+---------+
or grab latest non NULL value for balloons.
This works for all versions of MySQL
select i.*
from inventory i
join
(
select store, max(invDate) as maxDate
from inventory
group by store
) tmp on tmp.store = i.store
and tmp.maxDate = i.invDate
With MySQL 8+ you can do window functions:
with cte as
(
select store, widgets, balloons,
ROW_NUMBER() OVER(PARTITION BY store ORDER BY invDate desc) AS rn
from inventory
)
select * from cte where rn = 1
You can use a correlated sub query to get latest record for each store
select i.*
from inventory i
where i.invDate = (
select max(invDate)
from inventory
where i.store = store
)
order by i.store
DEMO
I have a table called tbl_chat and tbl_post.
The tbl_chat is given as follows.
|--------------------------------------------------------------------------------|
| chat_id | message | from_user | to_user | post_id |send_date |
|--------------------------------------------------------------------------------|
| 1 | Hi | 23 | A | 35 | 2016-04-01 17:35|
| 2 | Test | 24 | A | 35 | 2016-04-02 01:35|
| 3 | Thut | A | 23 | 35 | 2016-04-02 03:35|
| 4 | test | A | 24 | 35 | 2016-04-02 12:35|
| 5 | Hi | 23 | A | 35 | 2016-04-03 17:35|
|--------------------------------------------------------------------------------|
Now, in the chat table we can see three users are interacting with each other. The Admin (A), user with id = 23 and user = 24.
So there is basically two chat thread.
One between A and 23
Another between A and 24.
I want a query which will show the two chat threads, with the last chat message. Just like in case of facebook chat list showing all the chat-threads with mentioning the last chat.
I am writing a query like this.
SELECT * FROM tbl_chat, tbl_post
WHERE tbl_post.post_id = tbl_chat.post_id
AND tbl_post.post_id = '39'
GROUP BY tbl_chat.chat_from
ORDER BY date DESC
The query has a problem. It is first retrieving all the chats and grouping it w.r.t. chat_from and then ORDERING it Descending-wise.
So first it's creating the group, and then ordering the group.
Also, the first query produces three group, taking the reply message from Admin as a separate group. Since GROUP BY chat_from.
How can I solve this issue?
EDIT:-
I would be grateful if someone can build the query in Active-Records of Codeigniter.
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(chat_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,message VARCHAR(20) NOT NULL
,from_user VARCHAR(12)
,to_user VARCHAR(12)
,post_id INT NOT NULL
,send_date DATETIME NOT NULL
);
INSERT INTO my_table VALUES
(1,'Hi' ,'23','A' ,35,'2016-04-01 17:35:00'),
(2,'Test','24','A' ,35,'2016-04-02 01:35:00'),
(3,'Thut','A' ,'23',35,'2016-04-02 03:35:00'),
(4,'test','A' ,'24',35,'2016-04-02 12:35:00'),
(5,'Hi' ,'23','A' ,35,'2016-04-03 17:35:00');
SELECT a.*
FROM my_table a
JOIN
( SELECT LEAST(from_user,to_user) user1
, GREATEST(from_user,to_user) user2
, MAX(send_date) send_date
FROM my_table
GROUP
BY user1
, user2
) b
ON b.user1 = LEAST(a.from_user,a.to_user)
AND b.user2 = GREATEST(a.from_user,a.to_user)
AND b.send_date = a.send_date;
+---------+---------+-----------+---------+---------+---------------------+
| chat_id | message | from_user | to_user | post_id | send_date |
+---------+---------+-----------+---------+---------+---------------------+
| 4 | test | A | 24 | 35 | 2016-04-02 12:35:00 |
| 5 | Hi | 23 | A | 35 | 2016-04-03 17:35:00 |
+---------+---------+-----------+---------+---------+---------------------+
You can use NOT EXISTS() :
SELECT * FROM tbl_chat
INNER JOIN tbl_post
ON tbl_post.post_id = tbl_chat.post_id
WHERE NOT EXISTS(SELECT 1 FROM tbl_chat s
WHERE tbl_chat.from_user IN(s.from_user,s.to_user)
AND tbl_chat.to_user IN(s.from_user,s.to_user)
AND tbl_chat.date < s.date)
Although date field looks like DATE type, which is curious - how would you find the difference between two messages at the same day?
This is similar to the table I have:
+-----------+--------+
| Player id | Score |
+-----------+--------+
| User 1 | 10 |
| User 2 | 22 |
| User 1 | 17 |
| User 3 | 34 |
| User 4 | 23 |
| User 2 | 12 |
| User 3 | 10 |
| User 4 | 30 |
+-----------+--------+
I also have a timestamp on each row.
What is the best way to get the most recent row for each 'player'. like the following result:
+-----------+--------+
| Player id | Score |
+-----------+--------+
| User 1 | 10 |
| User 2 | 22 |
| User 3 | 34 |
| User 4 | 23 |
+-----------+--------+
Try this query
SELECT Player_id,
SUBSTRING_INDEX(GROUP_CONCAT(score ORDER BY date_added DESC),',',1) AS recent_score,
SUBSTRING_INDEX(GROUP_CONCAT(date_added ORDER BY date_added DESC),',',1) AS recent_date
FROM table
GROUP BY Player_id
Here I have assumed your timestamp field as date_added, and also score by recent date
I think you can use a query like this:
(time field is your timestamp field that elaborate recent record)
SELECT
`t1`.`Player id`, `t1`.`Score`, `t1`.`time field`
FROM `yourTable` As `t1`
JOIN
`yourTable` As `t2`
ON `t1`.`Player id` = `t2`.`Player id`
AND `t1`.`time field` <= `t2`.`time field`
GROUP BY
`t1`.`Player id`, `t1`.`Score`, `t1`.`time field`
HAVING
COUNT(`t2`.`Player id`) = 1;
[SQL Fiddle Demo]
I think this can help you:
SELECT
<Player_id>, <Score>
FROM
<your_table>
WHERE
(<clientID>,<timestamp>) IN (
SELECT
<Player_id>, MAX(<timestamp>)
FROM
<your_table>
GROUP BY
<Player_id>
);
Just replace the <enclosed> part with the corresponding column name of your table.
I have 2 tables
requests
requests_votes
request structure is:
id | req_name | content | hits
------------------------------
1 | Sample | Some | NULL
2 | Sample | Some | NULL
3 | Sample | Some | NULL
4 | Sample | Some | NULL
5 | Sample | Some | NULL
requests_votes structure is:
id | requestid | user_id
------------------------
10 | 2 | 2556
18 | 2 | 2522
33 | 3 | 120
44 | 2 | 1559
98 | 5 | 253
width the following query im able to calculate how votes have an item(request)
SELECT `requestid` , count( requests_votes.id ) AS totals
FROM `requests_votes`
INNER JOIN `requests` ON requests.id = requests_votes.requestid
GROUP BY `requestid`
output:
requestid | totals
------------------
2 | 3
3 | 1
5 | 1
i want to populate the hits column inside requests table with count( requests_votes.id ) result.
I've go trough the similar questions asked here on stackoverflow, but couldn't find the solution.
You can use JOIN in an UPDATE query:
UPDATE `requests` T1 JOIN
(SELECT `requestid` , count( requests_votes.id ) AS totals
FROM `requests_votes`
INNER JOIN `requests` ON requests.id = requests_votes.requestid
GROUP BY `requestid`) T2 ON T1.id=T2.requestid
SET T1.hits=T2.totals
I have a table
________________________________________________________________________________
| Message |
|_______________________________________________________________________________|
| ID(INT) | Text(TEXT) |read(TINYINT(0/1)) |deleted(TINYINT(0/1))| User_id |
| 1 | How Are You?| 0 | 0 | 6 |
| 2 | Fine | 0 | 1 | 4 |
| 3 | Message 3 | 1 | 0 | 6 |
| 4 | Message 4 | 0 | 1 | 6 |
| 5 | Message 5 | 1 | 0 | 5 |
|_________|_____________|___________________|_____________________|_____________|
Now I want to select the message where user_id=6 and also select the column count for read=0 and read=1 seperately.I know this can be done by group by command and iam currently doing it with two sql queries .May somebody join them in one
select message,count(*) from message where User_id=6 and read=0 group by id;//for unread message
select message,count(*) from message where User_id=6 and read=1 group by id;//for read message
Joining your two queries is quite easy, but I don't get what you really want.
SELECT `message`, count(*) AS `cnt` , `read`
FROM message
WHERE `User_id`=6
AND `read` IN ( 0, 1 )
GROUP BY `id` , `read`;
Why don't you
select
message,
sum(read) as num_read,
sum(case read when 0 then 1 else 0 end) as num_unread
from message
where user_id = 6
group by user_id
? You can drop either the where or the group by line, depending on your real need and SQL implementation.
Try this:
select read,id,count(*)
from message
where user_id = 6 and read in (0,1) group by read,id