Modifying the group by column to get the newest result on top - mysql

I am sorry if this has been already posted or its on internet. I came here after long search
Suppose this is the table:
+----+-------+----------+---------------------+
| id | name | group_id | created_time |
+----+-------+----------+---------------------+
| 1 | foo | 1 | 2010-09-22 00:00:00 |
| 2 | rafi | 2 | 2010-09-23 00:00:00 |
| 3 | rafi1 | 2 | 2010-09-24 00:00:00 |
| 4 | rafi2 | 2 | 2010-09-25 00:00:00 |
| 5 | bar | 5 | 2010-09-26 00:00:00 |
| 6 | baz | 6 | 2010-09-27 00:00:00 |
| 7 | baz1 | 6 | 2010-09-26 00:00:00 |
| 8 | rafi3 | 2 | 2010-09-24 00:00:00 |
| 9 | baz2 | 6 | 2010-09-30 00:00:00 |
+----+-------+----------+---------------------+
What I want is to group these according to group ids and order it by created_time desc(newer first)
but when i say
SELECT id,name,group_id,created_time FROM test group by group_id ORDER BY id desc;
I get this
+----+------+----------+---------------------+
| id | name | group_id | created_time |
+----+------+----------+---------------------+
| 6 | baz | 6 | 2010-09-27 00:00:00 |
| 5 | bar | 5 | 2010-09-26 00:00:00 |
| 2 | rafi | 2 | 2010-09-23 00:00:00 |
| 1 | foo | 1 | 2010-09-22 00:00:00 |
+----+------+----------+---------------------+
what i want is to get something like this
+----+------+----------+---------------------+
| id | name | group_id | created_time |
+----+------+----------+---------------------+
| 9 | baz2 | 6 | 2010-09-30 00:00:00 |
| 5 | bar | 5 | 2010-09-26 00:00:00 |
| 5 | rafi2| 2 | 2010-09-25 00:00:00 |
| 1 | foo | 1 | 2010-09-22 00:00:00 |
+----+------+----------+---------------------+
I have tried
SELECT max(date(created_time)) as foo,name,group_id FROM test group by group_id ORDER BY foo desc;
I get the dates right but cant get the name right.

does that query fit your needs?
SELECT t1.id, t1.name, t1.group_id FROM Test t1
INNER JOIN
(SELECT MAX(id) as maxid FROM Test GROUP BY group_id) t2
ON t2.maxid = t1.id
ORDER BY t1.id DESC;
EDIT:
if you want to order by a datetime field you can slightly modify the query above:
SELECT t1.id, t1.name, t1.group_id, t1.created_date FROM Test t1
INNER JOIN
(SELECT MAX(created_date) as maxdate, group_id FROM Test GROUP BY group_id) t2
ON (t2.maxdate = t1.created_date AND t2.group_id = t1.group_id)
ORDER BY t1.created_date DESC;
Is it still what you're looking for?

How about this? Is using a subquery an option - if so, this might work!
SELECT id, name, group_id
WHERE id
IN (Select max(id) FROM test group by group_id)
ORDER BY id desc;

Related

mysql table ordering incorrect with group by and order by

table 1: forum_threads
+-----+------+-------+
| id | title| status|
+-----+------+-------+
| 1 | a | 1 |
| 2 | b | 1 |
| 3 | c | 1 |
| 4 | d | 1 |
| 5 | e | 1 |
| 6 | f | 1 |
+-----+------+-------+
table 2: forum_comments
+-----+----------+--------------------+
| id | thread_id| comment |
+-----+----------+--------------------+
| 1 | 4 | hai |
| 2 | 4 | hello |
| 3 | 2 | welcome |
| 4 | 2 | whats your name |
| 5 | 6 | how are you |
| 6 | 5 | how old are you |
| 7 | 5 | good |
+-----+----------+--------------------+
wanted output
+-----------+----------+-----------------+
| thread_id | title | comment_count |
+-----------+----------+-----------------+
| 5 | e | 2 |
| 6 | f | 1 |
| 2 | b | 2 |
| 4 | d | 2 |
+-----------+----------+-----------------+
my Query
SELECT forum_threads.*,forum_comments.*,count(forum_comments.id) as comment_count
FROM forum_comments
LEFT JOIN forum_threads ON forum_comments.thread_id = forum_threads.id
GROUP BY forum_threads.id
ORDER BY forum_comments.id desc
Here I am trying to get the titles by the latest comment.
when I give ORDER BY forum_comments.id this returns the wrong order.
I need to order by the latest comments in the forum_comments table.
this query returns the wrong order please help me to find out the correct order.
how could I solve this easily?
This query should give you the expected result:
select t2.thread_id, t1.title, t2.comment_count from forum_threads as t1,
(SELECT id, thread_id, count(comment) as comment_count from forum_comments group by thread_id) as t2
where t1.id = t2.thread_id order by t2.id desc;
Instead of using forum_threads.* and forum_comments.* can you give specific column names and try.
If that doesn't work you should try explicitly assigning primary and foreign keys.

mysql group by keeping the more recent date

I have the following table:
+------------+--------+-----+
| reg_dat | status | id |
+------------+--------+-----+
| 2016-01-31 | 10 | 1 |
| 2017-06-31 | 12 | 1 |
| 2015-01-31 | 12 | 4 |
| 2017-01-25 | 5 | 4 |
| 2017-01-11 | 3 | 2 |
+------------+--------+-----+
I would like to do a mysql query to group the rows by id and keeping only the more recent date... so the output should be the following:
+------------+--------+-----+
| reg_dat | status | id |
+------------+--------+-----+
| 2017-06-31 | 12 | 1 |
| 2017-01-25 | 5 | 4 |
| 2017-01-11 | 3 | 2 |
+------------+--------+-----+
Unfortunately my code doesn't work...
select *
from table
group by id
order by id, reg_dat DESC
Have you some suggestions?
You can do that using a JOIN and a subquery
SELECT t.reg_dat, t.status, t.id
FROM table t
JOIN (SELECT max(reg_dat) max_date, id FROM table GROUP BY id) t1
ON t.reg_dat = t1.max_date AND t.id = t1.id

Select most recent MAX() and MIN() - WebSQL

i'm build an exercises web app and i'm working with two tables like this:
Table 1: weekly_stats
| id | code | type | date | time |
|----|--------------|--------------------|------------|----------|
| 1 | CC | 1 | 2015-02-04 | 19:15:00 |
| 2 | CC | 2 | 2015-01-28 | 19:15:00 |
| 3 | CPC | 1 | 2015-01-26 | 19:15:00 |
| 4 | CPC | 1 | 2015-01-25 | 19:15:00 |
| 5 | CP | 1 | 2015-01-24 | 19:15:00 |
| 6 | CC | 1 | 2015-01-23 | 19:15:00 |
| .. | ... | ... | ... | ... |
Table 2: global_stats
| id | exercise_number |correct | wrong |
|----|-----------------|--------|-----------|
| 1 | 138 | 1 | 0 |
| 2 | 246 | 1 | 0 |
| 3 | 988 | 1 | 10 |
| 4 | 13 | 5 | 0 |
| 5 | 5 | 4 | 7 |
| 6 | 5 | 4 | 7 |
| .. | ... | ... | ... |
What i would like is to get MAX(correct-wrong) and MIN(correct-wrong) and now i'm working with this query:
SELECT
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MAX(correct - wrong) from global_stats)
UNION
SELECT
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MIN(correct - wrong) from global_stats);
This query is working good, except for one thing: when "WHERE correct - wrong = (SELECT MIN(correct - wrong)[...]" selects more than one row, the row selected is the first but i would like to have returned the most recent (in other words: ordered by datetime(date, time)). Is it possible?
Thanks!
I think you can solve it like this:
SELECT * FROM (
SELECT
1 as sort_column,
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MAX(correct - wrong) from global_stats)
ORDER BY date DESC, time DESC
LIMIT 1 ) as a
UNION
SELECT * FROM (
SELECT
2 as sort_column,
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MIN(correct - wrong) from global_stats)
ORDER BY date DESC, time DESC
LIMIT 1) as b
ORDER BY sort_column;
Here is the documentation about how UNION works.

MySQL filter first date for last user for each item

I have this problem that I've been struggling with for awhile.
I have this data:
+----+---------+---------+---------------------+
| id | file_id | user_id | action_datetime |
+----+---------+---------+---------------------+
| 1 | 1 | 2 | 2014-03-13 13:39:31 |
| 2 | 1 | 2 | 2014-03-13 13:39:43 |
| 3 | 1 | 1 | 2014-03-13 13:59:34 |
| 4 | 1 | 1 | 2014-03-13 14:01:38 |
| 5 | 1 | 1 | 2014-03-13 14:03:12 |
| 6 | 2 | 2 | 2014-03-13 14:04:51 |
| 7 | 2 | 2 | 2014-03-13 14:07:37 |
| 8 | 1 | 1 | 2014-03-13 14:08:04 |
| 9 | 3 | 1 | 2014-03-13 14:08:09 |
| 10 | 3 | 1 | 2014-03-13 14:08:12 |
| 11 | 4 | 1 | 2014-03-13 14:08:14 |
| 12 | 4 | 1 | 2014-03-13 14:08:16 |
| 13 | 5 | 1 | 2014-03-13 14:08:26 |
| 14 | 1 | 1 | 2014-03-13 14:08:40 |
| 15 | 2 | 2 | 2014-03-13 14:09:13 |
+----+---------+---------+---------------------+
I need to select rows with first date of last user for each file, i.e rows i need are:
+----+---------+---------+---------------------+
| id | file_id | user_id | action_datetime |
+----+---------+---------+---------------------+
| 3 | 1 | 1 | 2014-03-13 13:59:34 |
| 6 | 2 | 2 | 2014-03-13 14:04:51 |
| 9 | 3 | 1 | 2014-03-13 14:08:09 |
| 11 | 4 | 1 | 2014-03-13 14:08:14 |
| 13 | 5 | 1 | 2014-03-13 14:08:26 |
+----+---------+---------+---------------------+
I tried few things along the lines of query below, but it's no good, I know. Please help.
SELECT
t1.id as id,
t1.file_id as file_id,
t1.user_id as user_id,
t1.action_datetime as datetime
FROM `table_1` as t1
WHERE t1.`id` IN (SELECT MIN(id) FROM `table_1` GROUP BY `file_id`)
SQL Fiddle HERE
Thanks in advance.
Here you go:
SELECT t.* FROM (
SELECT t2.`file_id`, t2.`user_id`, MIN(t2.`action_datetime`) ts_first_date FROM (
SELECT t1.*
FROM table_1 t1
JOIN (
SELECT `file_id`, MAX(`action_datetime`) as ts_last_user
FROM table_1
GROUP BY `file_id`
) tmp2
ON tmp2.`file_id` = t1.`file_id` AND tmp2.ts_last_user = t1.action_datetime
) t3
JOIN table_1 t2
ON t3.`file_id` = t2.`file_id` AND t3.`user_id` = t2.`user_id`
GROUP BY t2.`file_id`, t2.`user_id`
) outertable
JOIN
table_1 t
ON t.user_id = outertable.user_id AND t.file_id = outertable.file_id AND t.action_datetime = outertable.ts_first_date
ORDER BY t.file_id
I know its hard to understand but still it works :)
Working Fiddle: http://sqlfiddle.com/#!2/ba935f/105
Inner most join is used to find the pair of file_id and user_id to get who was to last user on a file. Then you use this pair in other joins to get the first access date for this particular group.
Agree with G one’s comment, the first line in your desired output should have user_id=2 with action_datetime=2014-03-13 13:39:31, otherwise it does not seem to make sense. (Or you did not phrase your problem so that we could understand it correctly.)
Do get that, you can use the approach described in the MySQL manual as The Rows Holding the Group-wise Maximum of a Certain Column:
SELECT
t1.id AS id,
t1.file_id AS file_id,
t1.user_id AS user_id,
t1.action_datetime AS datetime
FROM table_1 AS t1
LEFT JOIN table_1 t2
ON t1.file_id = t2.file_id
AND t1.action_datetime > t2.action_datetime
WHERE t2.id IS NULL;
http://sqlfiddle.com/#!2/ba935f/62
Edit: OK, if you really want row #3 on the base of it being the “first date for the last user”, then you will have to apply this approach two times, nested into each other: First to get the IDs of the last users, and then again to get the minimum date based on the combination of file and user.

Filter out closest duplicated rows (but not completely all) from MySQL table

In table I need to filter out nearest duplicated rows which have same status_id (but not completely all) when user_id is the same. GROUP BY or DISTINCT did not help in this situation. Here is an example:
---------------------------------------------------
| id | user_id | status_id | date |
---------------------------------------------------
| 1 | 10 | 1 | 2010-10-10 10:00:10|
| 2 | 10 | 1 | 2010-10-11 10:00:10|
| 3 | 10 | 1 | 2010-10-12 10:00:10|
| 4 | 10 | 2 | 2010-10-13 10:00:10|
| 5 | 10 | 4 | 2010-10-14 10:00:10|
| 6 | 10 | 4 | 2010-10-15 10:00:10|
| 7 | 10 | 2 | 2010-10-16 10:00:10|
| 8 | 10 | 2 | 2010-10-17 10:00:10|
| 9 | 10 | 1 | 2010-10-18 10:00:10|
| 10 | 10 | 1 | 2010-10-19 10:00:10|
Have to look like:
---------------------------------------------------
| id | user_id | status_id | date |
---------------------------------------------------
| 1 | 10 | 1 | 2010-10-10 10:00:10|
| 4 | 10 | 2 | 2010-10-13 10:00:10|
| 5 | 10 | 4 | 2010-10-14 10:00:10|
| 7 | 10 | 2 | 2010-10-16 10:00:10|
| 9 | 10 | 1 | 2010-10-18 10:00:10|
Oldest entries (by date) should remain in the table
You want to keep each row where the previous status is different, based on the id or date column.
If your ids are really sequential (as they are in the question), you can do this with a convenient join:
select t.*
from t left outer join
t tprev
on t.id = tprev.id+1
where tprev.id is null or tprev.status <> t.status;
If the ids are not sequential, you can get the previous one using a correlated subquery:
select t.*
from (select t.*,
(select t2.status
from t t2
where t2.user_id = t.user_id and
t2.id < t.id
order by t2.id desc
limit 1
) as prevstatus
from t
) t
where prevstatus is null or prevstatus <> t.status;