getting last interaction from 3 tables - mysql

I have 3 tables that look somewhat like this:
tbl_rating_yes
ID, rater, rated, ratetype, mry_timestamp
1 1234 3456 3 '2015-06-01 23:01:00:000'
2 1234 7666 3 '2013-06-01 23:02:00:000'
3 5678 1234 3 '2013-06-01 23:14:00:000'
4 9085 4567 3 '2013-06-01 23:19:00:000'
tbl_rating_no
ID, rater, rated, ratetype, mrn_timestamp
1 6679 1234 1 '2015-06-01 13:09:00:000'
2 1234 9900 1 '2015-06-01 13:10:00:000'
3 8891 1111 1 '2015-06-01 14:56:00:000'
4 0010 4545 1 '2015-06-01 15:06:00:000'
tbl_rating_maybe
ID, rater, rated, ratetype, mrm_timestamp
1 5632 9000 2 '2013-06-01 11:00:59:000'
2 3421 8787 2 '2013-06-01 12:44:00:000'
3 0100 9887 2 '2013-06-01 16:04:00:000'
4 9085 1234 2 '2013-06-02 01:22:00:000'
i'd like to be able to find out the last interaction that rater 9085 did.
currently i have a query like:
SELECT GREATEST(IFNULL(`mrm_timestamp`,'1900-01-01 01:01:01'),
IFNULL(`mrn_timestamp`,'1900-01-01 01:01:01'),
IFNULL(`mry_timestamp`,'1900-01-01 01:01:01')) as timestamp
FROM tbl_rating_no rn
LEFT JOIN tbl_rating_yes ry
ON ry.`rater` = rn.`rater`
LEFT JOIN tbl_rating_maybe rm
ON rm.`rater` = rn.`rater`
WHERE rn.rater = 9085
ORDER BY timestamp
but since 9085 doesn't exist in the no rating table, it doesn't bring back a result. I'd like to be able to bring back the timestamp, the ratetype and the id at the least. there shouldn't be clashes but if there are bring back all the results for the max time. so this should ideally bring back the information from tbl_rating_maybe.

Use union all and limit:
select r.*
from ((select r.*
from tbl_rating_yes r
where r.rater = 9085
order by mry_timestamp desc
limit 1
)
union all
(select r.*
from tbl_rating_no r
where r.rater = 9085
order by mrn_timestamp desc
limit 1
)
union all
(select r.*
from tbl_rating_maybe r
where r.rater = 9085
order by mrm_timestamp desc
limit 1
)
) r
order by mry_timestamp desc
limit 1;
In fact, having multiple tables with essentially the same structure is typically a bad idea. You should consider restructuring your tables by combining these three into a single table, with a column containing the rating.

Related

Select multiple tables with only unique users and ordered by latest id

I have 2 tables, first one is called members:
id name show
1 John 1
2 Wil 1
3 George 1
4 Chris 1
Second is called score:
id user_id score
1 1 90
2 1 70
3 2 55
4 3 30
5 3 40
6 3 100
7 4 30
user_id from score is the id of members.
What I want is to show a scorelist with unique members.id, ordered by score.score and order by the latest score.id.
I use the following code:
SELECT members.id, members.show, score.id, score.user_id, score.score FROM members
INNER JOIN score ON score.user_id = members.id
WHERE members.show = '1'
GROUP BY score.user_id
ORDER BY score.score DESC, score.id DESC
The output is not ordered by the latest score.id, but it does show only unique user_id's:
id user_id score
1 1 90
3 2 55
4 3 30
7 4 30
It should be like:
id user_id score
6 3 100
2 1 70
3 2 55
7 4 30
I hope you can help me
You could use:
with cte as (
select id,
user_id,
score,
row_number() over(partition by user_id order by id desc) as row_num
from score
) select cte.id,user_id,score
from cte
inner join members m on cte.user_id=m.id
where row_num=1
order by score desc;
Demo
If your MySQL server doesn't support windows function, use:
select s.id,s.user_id,s.score
from score s
inner join members m on s.user_id=m.id
where s.id in (select max(id) as id
from score
group by user_id
)
order by score desc;
Demo

Get most recent groups ordered by users last login date DESC

The title makes it sound easy but what I'd like to do is get the last 20 groups from the groups table, ordered by their corresponding users last login date. A group can have one or more users and ultimately what I want to do is find out which groups have had the least user activity and retrieve the last login date.
Here's the query I came up with -
SELECT DISTINCT g.name, user_max.max_login_last_at FROM groups g
LEFT JOIN group_user gu on g.id = gu.group_id
LEFT JOIN (
SELECT MAX(login_last_at) max_login_last_at, u.id
FROM users u GROUP BY id
) AS user_max ON (user_max.id = gu.user_id)
ORDER BY user_max.max_login_last_at ASC
The problem is when I perform the join, it pulls in every group_user record and results in duplicates. I feel like there could be an easy solution to this one but I can't seem to figure it out!
groups table
id name
1 Group 1
2 Group 2
users table
id email login_last_at
1 user1#example.com 2018-10-17 16:08:47
2 user2#example.com 2018-10-02 15:41:53
3 user3#example.com NULL
4 user4#example.com 2018-10-08 12:01:48
5 user5#example.com 2018-10-15 9:24:57
6 user6#example.com 2018-10-17 11:10:58
7 user7#example.com 2018-10-17 15:33:03
group_user table
id group_id user_id
1 1 1
2 2 1
3 1 2
4 1 3
5 1 4
6 2 5
7 1 5
8 2 6
9 1 7
Current example result -
name max_login_last_ts
Group 1 2018-10-02 15:41:53
Group 1 2018-10-08 12:01:48
Group 2 2018-10-15 09:24:57
Group 1 2018-10-15 09:24:57
Group 2 2018-10-17 11:10:58
Group 1 2018-10-17 15:33:03
Group 1 2018-10-17 16:08:47
Group 2 2018-10-17 16:08:47
Group 1 2018-10-18 08:55:17
The problem is as you can see in the result above is that I'm getting all groups, all I really want is the following -
name max_login_last_ts
Group 2 2018-10-17 16:08:47
Group 1 2018-10-18 08:55:17
Thanks in advance!
I think this query will do what you want. There isn't enough data in your sample to replicate your desired results though.
SELECT g.name, MAX(u.login_last_at) AS max_login_last_at
FROM `groups` g
JOIN group_user gu on gu.group_id = g.id
JOIN users u ON u.id = gu.user_id
GROUP BY g.name
ORDER By max_login_last_at DESC
LIMIT 20
Demo on dbfiddle

Select difference based on record having minimum and maximum date in MySql

Below is my table let's call account
**ID accountID score tracking_date
1 1 3 2014-09-25 00:01:05
2 2 4 2014-09-26 01:05:18
3 1 6 2014-09-27 09:23:05
4 2 9 2014-09-28 20:01:05
5 1 1 2014-09-28 23:21:34
6 3 7 2014-09-21 00:01:00
7 2 1 2014-09-22 01:45:24
8 2 9 2014-09-27 14:01:43
9 3 1 2014-09-24 22:01:27
I want to select record with max date and also the difference of score with the records having tracking_date as minimum for that accountId. So I want output like below
ID accountID score_with_maxdate diff_score_with_mindate max_tracking_date
1 1 1 -2 2014-09-28 23:21:34
2 2 9 8 2014-09-28 20:01:05
3 3 1 -6 2014-09-24 22:01:27
Any help?
Here is one option. We can self-join a subquery which finds both the min and max tracking dates, for each account, twice to your original table. This will bring in all metadata for those max tracking date records, including the scores.
SELECT
t1.accountID,
t2.score AS score_with_maxdate,
t2.score - t3.score AS diff_score_with_mindate,
t1.max_tracking_date
FROM
(
SELECT
accountID,
MAX(tracking_date) AS max_tracking_date,
MIN(tracking_date) AS min_tracking_date
FROM yourTable
GROUP BY accountID
) t1
INNER JOIN yourTable t2
ON t1.accountId = t2.accountID AND t2.tracking_date = t1.max_tracking_date
INNER JOIN yourTable t3
ON t1.accountId = t3.accountID AND t3.tracking_date = t1.min_tracking_date
ORDER BY
t1.accountID;
Demo
This is a somewhat tricky question. I think conditional aggregation is a convenient way to solve the problem:
select min(t.id) as id, t.accountId,
max(case when t.tracking_date = t2.max_td then t.score end) as score_with_maxdate,
max(case when t.tracking_date = t2.max_td then t.score
when t.tracking_date = t2.min_td then - t.score
end) as diff_score_with_mindate,
max(t.tracking_date) as max_tracking_date
from t join
(select t2.accountId, min(t2.tracking_date) as min_td, max(t2.tracking_date) as max_td
from t t2
group by t2.accountId
) t2
on t.accountId = t2.accountId
group by t.accountId;
Another hackish way of getting same results by using aggregate and string fucntion
select t.accountID,
t.score_with_maxdate,
t.score_with_maxdate - t.score_with_mindate score_with_maxdate,
t.max_tracking_date
from(
select accountID,
substring_index(group_concat(score order by tracking_date desc),',', 1) + 0 score_with_maxdate,
substring_index(group_concat(score order by tracking_date asc),',', 1) + 0 score_with_mindate,
max(tracking_date) max_tracking_date
from demo
group by accountID
) t
Demo
But i would suggest you to go with other solutions mentioned by Tim & Gordon

How to get rank in MySQL from 2 tables?

I have 2 different tables in my database by the name of: rank, settings.
Here is how each table looks like with a few records in them:
Table #rank:
id points userid
-- ----- ------
1 500 1
2 300 2
3 900 3
4 1500 4
5 100 5
6 700 6
7 230 7
8 350 8
9 850 9
10 150 10
Table #settings:
userid active
------ ------
1 0
2 1
3 1
4 1
5 1
6 0
7 1
8 1
9 0
10 1
I want to get the rank of a specific user by user_id from the rank table ordering by their points. Also I would Only want to include the users in the ranking results, if they have active = 1 set in the settings table.
I have a simple ranking query, but it is not really effective, because it does include everyone even if the user is not active:
SELECT * FROM
(SELECT #sort:=#sort+1 AS sort, points, userid
FROM rank,
(SELECT #sort := 0) s
ORDER BY points DESC) t
WHERE userid= 8
Any idea, how could I achieve my goals here?
Few sub queries. First gets all the users who are active in the right order. That is used as a source for another query to add the rank. Then this is used as the source for the points and rank for the userid you are actually interested in
SELECT sort, points
FROM
(
SELECT #sort:=#sort + 1 AS sort, points, userid
FROM
(
SELECT rank.points, rank.userid
FROM rank
INNER JOIN settings
ON rank.userid = settings.userid
WHERE settings.active = 1
ORDER BY points DESC
) sub0
CROSS JOIN (SELECT #sort:=0) sub2
) sub1
WHERE sub1.userid = 8
Borrowing the idea from: https://stackoverflow.com/a/4474389/92063
SELECT
#rn:=#rn+1 AS RANK
,USER_ID
,POINTS
FROM (
SELECT
R.userid AS USER_ID
,R.points AS POINTS
FROM
rank R
INNER JOIN
settings S
ON R.userid = S.userid
WHERE
S.active = 1
ORDER BY
R.points DESC
) t1, (SELECT #rn:=0) t2;

How can I select the latest of three value types in a MySQL database?

Here is an example table:
ID time data type
0 0100 xyz 0
1 0200 xyz 1
2 0300 xyz 1
3 0400 xyz 2
4 0200 xyz 0
5 0500 xyz 2
6 0300 xyz 0
Data is added based on timestamp so that the last of each type has the latest time stamp.
I would like the result of a SELECT to be:
ID time data type
5 0500 xyz 2
2 0300 xyz 1
6 0300 xyz 0
And so the last of each 'type' is returned.
I can accomplish the above using three SELECT statements as follows:
SELECT time, data, type FROM table WHERE type=0 ORDER BY time DESC LIMIT 1
SELECT time, data, type FROM table WHERE type=1 ORDER BY time DESC LIMIT 1
SELECT time, data, type FROM table WHERE type=2 ORDER BY time DESC LIMIT 1
Is there a way to combine the above three SELECT statements into one?
Any help appreciated.
Chris
select m.ID, m.time, m.data, m.type
from (
select type, max(time) as MaxTime
from MyTable
group by type
) mm
inner join MyTable on mm.type = m.type
and mm.MaxTime = m.time