I want to sort the last messages with every user that a specific user has chated from ejabberd archive table.
The fields that I'm using are these
id (message id)
username (username copy)
bare_peer (user that is chatting with)
txt (text chat)
created_at (time created)
What I'm trying to achieve is something like that, but I need to group message by bare_peer with username as 1_usernode, but only the last messages.
I already tested a lot of queries, but none of them worked.
This is the first query I tried.
SELECT id, username, bare_peer, txt FROM archive where
username = '1_usernode' GROUP BY bare_peer ORDER BY created_at DESC;
And this is the output.
+------+------------+-------------------------------------------------------+---------------------+
| id | username | bare_peer | txt | created_at |
+------+------------+------------------------+------------------------------+---------------------+
| 1095 | 1_usernode | 10_usernode#localhost | Hello !!! | 2016-07-17 21:15:17 |
| 1034 | 1_usernode | 15_usernode#localhost | hey sup ? | 2016-07-13 22:40:29 |
| 1107 | 1_usernode | 13_usernode#localhost | oi | 2016-07-18 00:09:28 |
| 1078 | 1_usernode | 2_usernode#localhost | Hello this is just a Test!!! | 2016-07-15 16:30:50 |
| 1101 | 1_usernode | 7_usernode#localhost | hey | 2016-07-18 00:05:55 |
| 1084 | 1_usernode | 3_usernode#localhost | Hey how are you? | 2016-07-15 19:36:44 |
| 1085 | 1_usernode | 4_usernode#localhost | Hey how are you doing ? | 2016-07-17 19:20:00 |
Use This Query It's Helful.
SELECT MAX(id), username, bare_peer, txt FROM archive where
username = '1_usernode' ORDER BY created_at DESC
declare created_at as datetime
try this
DROP TABLE IF EXISTS `archive`;
CREATE TABLE `archive` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50) DEFAULT NULL,
`bare_peer` VARCHAR(50) DEFAULT NULL,
`txt` TEXT,
`created_at` DATETIME DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=latin1;
/*Data for the table archive */
INSERT INTO `archive`(`id`,`username`,`bare_peer`,`txt`,`created_at`)
VALUES (1034,'1_usernode','15_usernode#localhost','hey sup ?','2016-07-13 22:40:29'),
(1078,'1_usernode','2_usernode#localhost','Hello this IS just a Test!!!','2016-07-15 16:30:50'),
(1084,'1_usernode','3_usernode#localhost','Hey how are you?','2016-07-15 19:36:44'),
(1085,'1_usernode','4_usernode#localhost','Hey how are you doing ?','2016-07-17 19:20:00'),
(1095,'1_usernode','10_usernode#localhost','Hello !!!','2016-07-17 21:15:17'),
(1101,'1_usernode','7_usernode#localhost','hey','2016-07-18 00:05:55'),
(1107,'1_usernode','13_usernode#localhost','oi','2016-07-18 00:09:28');
Then run your query
SELECT id, username, bare_peer, txt FROM archive where
username = '1_usernode' GROUP BY bare_peer ORDER BY created_at DESC;
Try this query :-
SELECT archive.id, archive.max_id, archive.username, archive.bare_peer, archive.txt
FROM archive join
(SELECT MAX(id) max_id, username, bare_peer, txt
FROM archivewhere username = '1_usernode' GROUP BY bare_peer)
tab on archive.id=tab.max_id
Try this following code:-
select m.*
from
messages m
inner join (
select max(id) as maxid
from messages
group By (if(username > bare_peer, username, bare_peer)),
(if(username > bare_peer, bare_peer, username))
) t1 on m.id=t1.maxid ;
m is alias of message table
You want the entry with max(created_at) for every username and bare_peer.
One way to do that in MySQL is with 'having' but I don't like that.
I would first get the max(created_at) for every entry:
select username, bare_peer, max(created_at) as m_
from archive
group by username, bare_peer;
Then join the table on that result:
select b.*
from (
select username, bare_peer, max(created_at) as m_
from archive
group by username, bare_peer
) a
inner join archive as b on (
a.username = b.username
and a.bare_peer = b.bare_peer
and a.m_ = b.created_at
)
I want to know why it shows the created_at column when you don't select created_at? And I don't know why you use group by? There is nothing need to be divided into groups.
My statement looks like this.select id, username, bare_peer, txt, created_at from archive where username = '1_usercode' order by created_at desc
I created a temporary solution with the Rahauto's answer.
I put his query that returns me the correct id from latest message inside a
subquery so that i can extract message content from it's id.
SELECT username, bare_peer, txt, created_at FROM archive WHERE id IN (
SELECT tab.max_id FROM
archive JOIN (SELECT MAX(id) max_id, username, bare_peer, txt FROM
archive WHERE username = '1_usernode' GROUP BY bare_peer)
tab ON archive.id=tab.max_id
);
Related
Table: statistics
id | user | Message
----------------------
1 | user1 |message1
2 | user2 |message2
3 | user1 |message3
I am able to find the count of messages sent by each user using this query.
select user, count(*) from statistics group by user;
How to show message column data along with the count? For example
user | count | message
------------------------
user1| 2 |message1
|message3
user2| 1 |message2
You seem to want to show Count by user, which message sent by user.
If your mysql version didn't support window functions, you can do subquery to make row_number in select subquery, then only display rn=1 users and count
CREATE TABLE T(
id INT,
user VARCHAR(50),
Message VARCHAR(100)
);
INSERT INTO T VALUES(1,'user1' ,'message1');
INSERT INTO T VALUES(2,'user2' ,'message2');
INSERT INTO T VALUES(3,'user1' ,'message3');
Query 1:
SELECT (case when rn = 1 then user else '' end) 'users',
(case when rn = 1 then cnt else '' end) 'count',
message
FROM (
select
t1.user,
t2.cnt,
t1.message,
(SELECT COUNT(*) from t tt WHERE tt.user = t1.user and t1.id >= tt.id) rn
from T t1
join (
select user, count(*) cnt
from T
group by user
) t2 on t1.user = t2.user
) t1
order by user,message
Results:
| users | count | message |
|-------|-------|----------|
| user1 | 2 | message1 |
| | | message3 |
| user2 | 1 | message2 |
select user, count(*) as 'total' , group_concat(message) from statistics group by user;
You could join the result of your group by with the full table (or vice versa)?
Or, depending on what you want, you could use group_concat() using \n as separator.
Use Group_concat
select user, count(0) as ct,group_concat(Message) from statistics group by user;
This will give you message in csv format
NOTE: GROUP_CONCAT has size limit of 1024 characters by default in mysql.
For UTF it goes to 1024/3 and utfmb4 255(1024/4).
You can use group_concat_max_len global variable to set its max length as per need but take into account memory considerations on production environment
SET group_concat_max_len=100000000
Update:
You can use any separator in group_concat
Group_concat(Message SEPARATOR '----')
Try grouping with self-join:
select s1.user, s2.cnt, s1.message
from statistics s1
join (
select user, count(*) cnt
from statistics
group by user
) s2 on s1.user = s2.user
I am working on an problem regarding Selecting data from two MySQL tables.
First table holds messages | messages | (id, msg_group_id, to_user_id, from_user_id, datetime)
Second table holds user data | profiles | (user_id, name, firstname, ...)
ATM it works the way, that I can select ALL messages with a certain 'to_id' and by adding a JOIN statement getting the name and firstname of the user who sends the message.
My problem now is that I can not figure out a way to ONLY select the newest message of a certain msg_group_id.
I already tried GROUP BY msg_group_id combined with ORDER BY datetime DESC.
But that only throws the very first entry in message table. But I want to last one. :-)
I hope you can help me. :-)
My actual SQL statement:
SELECT LEFT(messages.message, 10) AS message,
`messages`.`msg_group_id`,
`messages`.`datetime`,
`profiles`.`name`,
`profiles`.`firstname`
FROM `messages`
LEFT JOIN `profiles`
ON `messages`.`from_user_id` = `profiles`.`user_id`
WHERE `to_user_id` = '2'
ORDER BY `datetime` DESC
LIMIT 20;
Thanks in Advance
Sample INPUT:
[messages]
|id|msg_group_id|to_user_is|from_user_id|message |datetime|
0 | 1 | 1 | 2 | Hello World1 | 2015-12-21 10:42:00
1 | 1 | 1 | 2 | Hello World2 | 2015-12-21 10:43:00
2 | 1 | 1 | 2 | Hello World3 | 2015-12-21 10:44:00
[profiles]
user_id|name |firstname|
1 | Test | User
2 | Thanks | Worldname
Result (what I don't want)
message|msg_group_id|datetime|name|firstname
Hello World1 | 1 | 2015-12-21 10:42:00 | Thanks | Worldname
Result (what I want)
message|msg_group_id|datetime|name|firstname
Hello World3 | 1 | 2015-12-21 10:44:00 | Thanks | Worldname
May be this query can help:
SELECT m.message, m.msg_group_id, m.datetime, u.name, u.firstname
FROM message as m, profiles as u
WHERE m.from_user_id = u.user_id
GROUP BY m.msg_group_id
ORDER BY m.datetime DESC
Or use INNER JOIN
SELECT m.message, m.msg_group_id, m.datetime, u.name, u.firstname
FROM message as m
INNER JOIN profiles as u ON m.from_user_id = u.user_id
GROUP BY m.msg_group_id
ORDER BY m.datetime DESC
I guess I solved the Problem with the help of another thread:
https://stackoverflow.com/a/1313140/4493030
My SQL Statement as follows:
SELECT `messages`.*, `profiles`.`nick_name`
FROM `messages`
LEFT JOIN `profiles`
ON `messages`.`from_user_id` = `profiles`.`user_id`
INNER JOIN
(SELECT konversation_id, MAX(id) AS maxid FROM messages
WHERE messages.to_user_id = 2
GROUP BY konversation_id) AS b
ON messages.id = b.maxid
WHERE `to_user_id` = '2'
ORDER BY `datetime` DESC
LIMIT 20;
Thanks to all of you who tried to help.
I found a way to tight it down
SELECT messages.to_user_id, messages.msg_group_id, MAX(messages.id) AS maxid, messages.from_user_id, profiles.name
FROM messages
LEFT JOIN profiles
ON messages.from_user_id = profiles.user_id
WHERE messages.to_user_id = 2
GROUP BY msg_group_id
I have a forum and I would like to see the latest topics with the author's name and the last user who answered
Table Topic (forum)
| idTopic | IdParent | User | Title | Text |
--------------------------------------------------------
| 1 | 0 | Max | Help! | i need somebody |
--------------------------------------------------------
| 2 | 1 | Leo | | What?! |
Query:
SELECT
Question.*,
Response.User AS LastResponseUser
FROM Topic AS Question
LEFT JOIN (
SELECT User, IdParent
FROM Topic
ORDER BY idTopic DESC
) AS Response
ON ( Response.IdParent = Question.idTopic )
WHERE Question.IdParent = 0
GROUP BY Question.idTopic
ORDER BY Question.idTopic DESC
Output:
| idTopic | IdParent | User | Title | Text | LastResponseUser |
---------------------------------------------------------------------------
| 1 | 0 | Max | Help! | i need somebody | Leo |
---------------------------------------------------------------------------
Example:
http://sqlfiddle.com/#!2/22f72/4
The query works, but is very slow (more or less 0.90 seconds over 25'000 record).
How can I make it faster?
UPDATE
comparison between the proposed solutions
http://sqlfiddle.com/#!2/94068/22
If using your current schema, I'd recommend adding indexes (particularly a clustered index (primary key)) and simplifying your SQL to let mySQL do the work of optimising the statement, rather than forcing it to run a subquery, sort the results, then run the main query.
CREATE TABLE Topic (
idTopic INT
,IdParent INT
,User VARCHAR(100)
,Title VARCHAR(255)
,Text VARCHAR(255)
,CONSTRAINT Topic_PK PRIMARY KEY (idTopic)
,CONSTRAINT Topic_idTopic_UK UNIQUE (idTopic)
,INDEX Topic_idParentIdTopic_IX (idParent, idTopic)
);
INSERT INTO Topic (idTopic, IdParent, User, Title, Text) VALUES
(1, 0, 'Max', 'Help!', 'i need somebody'),
(2, 1, 'Leo', '', 'What!?');
SELECT Question.*
, Response.User AS LastResponseUser
FROM Topic AS Question
LEFT JOIN Topic AS Response
ON Response.IdParent = Question.idTopic
WHERE Question.IdParent = 0
order by Question.idTopic
;
http://sqlfiddle.com/#!2/7f1bc/1
Update
In the comments you mentioned you only want the most recent response. For that, try this:
SELECT Question.*
, Response.User AS LastResponseUser
FROM Topic AS Question
LEFT JOIN (
select a.user, a.idParent
from Topic as a
left join Topic as b
on b.idParent = a.idParent
and b.idTopic > a.idTopic
where b.idTopic is null
) AS Response
ON Response.IdParent = Question.idTopic
WHERE Question.IdParent = 0
order by Question.idTopic
;
http://sqlfiddle.com/#!2/7f1bc/3
Assuming the highest IDTopic is the last responses user...
and assuming you want to return topics without responses...
Select A.IDTopic, A.IDParent, A.User, A.Title, A.Text,
case when b.User is null then 'No Response' else B.User end as LastReponseUser
FROM topic A
LEFT JOIN Topic B
on A.IdTopic = B.IDParent
and B.IDTopic = (Select max(IDTopic) from Topic
where IDParent=B.IDParent group by IDParent)
WHERE A.IDParent =0
I have a table of games, which is described as follows:
+---------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| date | date | NO | | NULL | |
| time | time | NO | | NULL | |
| hometeam_id | int(11) | NO | MUL | NULL | |
| awayteam_id | int(11) | NO | MUL | NULL | |
| locationcity | varchar(30) | NO | | NULL | |
| locationstate | varchar(20) | NO | | NULL | |
+---------------+-------------+------+-----+---------+----------------+
But each game has a duplicate entry in the table somewhere, because each game was in the schedules for two teams. Is there a sql statement I can use to look through and delete all the duplicates based on identical date, time, hometeam_id, awayteam_id, locationcity, and locationstate fields?
You should be able to do a correlated subquery to delete the data. Find all rows that are duplicates and delete all but the one with the smallest id. For MYSQL, an inner join (functional equivalent of EXISTS) needs to be used, like so:
delete games from games inner join
(select min(id) minid, date, time,
hometeam_id, awayteam_id, locationcity, locationstate
from games
group by date, time, hometeam_id,
awayteam_id, locationcity, locationstate
having count(1) > 1) as duplicates
on (duplicates.date = games.date
and duplicates.time = games.time
and duplicates.hometeam_id = games.hometeam_id
and duplicates.awayteam_id = games.awayteam_id
and duplicates.locationcity = games.locationcity
and duplicates.locationstate = games.locationstate
and duplicates.minid <> games.id)
To test, replace delete games from games with select * from games. Don't just run a delete on your DB :-)
You can try such query:
DELETE FROM table_name AS t1
WHERE EXISTS (
SELECT 1 FROM table_name AS t2
WHERE t2.date = t1.date
AND t2.time = t1.time
AND t2.hometeam_id = t1.hometeam_id
AND t2.awayteam_id = t1.awayteam_id
AND t2.locationcity = t1.locationcity
AND t2.id > t1.id )
This will leave in database only one example of each game instance which has the smallest id.
The best thing that worked for me was to recreate the table.
CREATE TABLE newtable SELECT * FROM oldtable GROUP BY field1,field2;
You can then rename.
To get list of duplicate entried matching two fields
select t.ID, t.field1, t.field2
from (
select field1, field2
from table_name
group by field1, field2
having count(*) > 1) x, table_name t
where x.field1 = t.field1 and x.field2 = t.field2
order by t.field1, t.field2
And to delete all the duplicate only
DELETE x
FROM table_name x
JOIN table_name y
ON y.field1= x.field1
AND y.field2 = x.field2
AND y.id < x.id;
select orig.id,
dupl.id
from games orig,
games dupl
where orig.date = dupl.date
and orig.time = dupl.time
and orig.hometeam_id = dupl.hometeam_id
and orig. awayteam_id = dupl.awayeam_id
and orig.locationcity = dupl.locationcity
and orig.locationstate = dupl.locationstate
and orig.id < dupl.id
this should give you the duplicates; you can use it as a subquery to specify IDs to delete.
AS long as you are not getting id (primary key) of the table in your select query and the other data is exact same you can use SELECT DISTINCT to avoid getting duplicate results.
delete from games
where id not in
(select max(id) from games
group by date, time, hometeam_id, awayteam_id, locationcity, locationstate
);
Workaround
select max(id) id from games
group by date, time, hometeam_id, awayteam_id, locationcity, locationstate
into table temp_table;
delete from games where id in (select id from temp);
DELETE FROM table
WHERE id =
(SELECT t.id
FROM table as t
JOIN (table as tj ON (t.date = tj.data
AND t.hometeam_id = tj.hometeam_id
AND t.awayteam_id = tj.awayteam_id
...))
DELETE FROM tbl
USING tbl, tbl t2
WHERE tbl.id > t2.id
AND t2.field = tbl.field;
in your case:
DELETE FROM games
USING games tbl, games t2
WHERE tbl.id > t2.id
AND t2.date = tbl.date
AND t2.time = tbl.time
AND t2.hometeam_id = tbl.hometeam_id
AND t2.awayteam_id = tbl.awayteam_id
AND t2.locationcity = tbl.locationcity
AND t2.locationstate = tbl.locationstate;
reference: https://dev.mysql.com/doc/refman/5.7/en/delete.html
I have one table with two queries and I need to sort it with descending type using ORDER BY. Here is my MySQL query that does not work properly:
(SELECT `text`
FROM `comments`
WHERE user_fr='".$user."' && archive='1'
ORDER BY `is_new_fr` DESC)
UNION
(SELECT `text`
FROM `message`
WHERE user_to='".$user."' && archive='1'
ORDER BY `is_new_to` DESC)
Description!
is_new_fr and is_new_to counts total new messages.
Here is my table contant:
user_fr | user_to | archive | is_new_fr | is_new_to| text
name1 | name2 | 1 | 2 | 0 | testing...
name2 | name1 | 1 | 0 | 5 | testing ...
I want to make an order that 1st will display note that has more messages to few, or by another words using DESCending type.
This is the display on the page I want to do:
Open dialog with name2. Messages (5)
Open dialog with name1. Messages (2)
Thank you!
The only way I know is a subquery:
SELECT `text`
FROM (
SELECT `text`, `is_new_fr` AS `is_new`
FROM `comments`
WHERE user_fr = '".$user."'
AND archive = '1'
UNION
SELECT `text`, `is_new_to` AS `is_new`
FROM `message`
WHERE user_to = '".$user."'
AND archive = '1'
) ORDER BY `is_new` DESC