Get last distinct value from 2 columns - mysql

I would like some help with this code. I'm trying to select the last chat message im MySQL. This is my table:
id
id_user_to
id_user_from
message
timestamp
1
1
2
Hi
1633345082
2
2
1
Hello
1633345083
3
1
3
Another
1633345088
4
3
1
Another2
1633345088
If I were to select the last message of user 1 I would get the second line, as it has the most recent timestamp. How can I do? I tried this code but I don't know how to adapt it to my context as I have to check that the user is taken once either in the from_user_id or to_user_id column, always with the most recent timestamp.
SELECT *
FROM [tableName]
WHERE id IN (
SELECT MAX(id)
FROM [tableName]
GROUP BY code
)

If you want the last message sent or received by a specific user :
select * from [tableName]
where id_user_to = 1 or id_user_from = 1
order by timestamp desc limit 1
ok for your second question:
select * from (
select *, row_number() over (partition by user1,user2 order by timestamp desc) rn
from (
select id ,id_user_to user1,id_user_from user2,message,timestamp
from table where id_user_to =1
union all
select id ,id_user_from, id_user_to,message,timestamp
from table where id_user_from =1
) t ) t where rn = 1
and If you remove conditions on userids inside sub queries , you get the latest messages for all the user ids

Related

SQL query for listing values based on a column

I have a table with the following columns member_id, status and created_at (timestamp) and i want to extract the latest status for each member_id based on the timestamp value.
member_id
status
created_at
1
ON
1641862225
1
OFF
1641862272
2
OFF
1641862397
3
OFF
1641862401
3
ON
1641862402
Source: Raw data image
So, my ideal query result would be like this:
member_id
status
created_at
1
OFF
1641862272
2
OFF
1641862397
3
ON
1641862402
Expected query results image
My go to process for doing things like that is to assign a row number to each data and get row number 1 depending on the partition and sorting.
For mysql, this is only available starting mysql 8
SELECT ROW_NUMBER() OVER(PARTITION BY member_id ORDER BY created_at DESC) as row_num,
member_id, status, created_at FROM table
This will generate something like this.
row_num
member_id
status
created_at
1
1
OFF
1641862272
2
1
ON
1641862225
1
2
OFF
1641862397
1
3
ON
1641862402
2
3
OFF
1641862401
Then you use that as a sub query and get the rows where row_num = 1
SELECT member_id, status, created_at FROM (
SELECT ROW_NUMBER() OVER(PARTITION BY member_id ORDER BY created_at DESC) as row_num,
member_id, status, created_at FROM table
) a WHERE row_num = 1
MySQL has support for Window Function since v8.0. the solution from crimson589 is preferred for v8+, this solution applies for earlier versions of MySQL or if you need an alternate solution to window queries.
After grouping by member_id we can either join back into the original set to gain the corresponding status value to the MAX(created_at)
SELECT ByMember.member_id
, status.status
, ByMember.created_at
FROM (
SELECT member_id, max(created_at) as created_at
FROM MemberStatus
GROUP BY member_id
) ByMember
JOIN MemberStatus status ON ByMember.member_id = status.member_id AND ByMember.created_at = status.created_at;
Or you could use a sub query instead of the join:
SELECT ByMember.member_id
, (SELECT status.status FROM MemberStatus status WHERE ByMember.member_id = status.member_id AND ByMember.created_at = status.created_at) as status
, ByMember.created_at
FROM (
SELECT member_id, max(created_at) as created_at
FROM MemberStatus
GROUP BY member_id
) ByMember
The JOIN based solution allows you to query additional columns from the original set instead of having multiple sub-queries. I would almost always advocate for the JOIN solution, but sometimes the sub-query is simpler to maintain.
I've setup a fiddle to compare these options: http://sqlfiddle.com/#!9/0edb931/11
You can group by member_id and max of created_at, then a self join with member_id and created_at will give you the latest status.

Finding rows with latest date by id mysql

I have these 2 tables I am trying to write a query that will help me select all rows that gives this result
users
id
name
1
test
2
test2
logs
id
userId
message
date
1
1
this is a test
2020-10-07 12:57:14
2
1
another reason
2020-10-07 13:57:14
3
1
another reason 2
2020-10-07 14:57:14
4
2
another reason 3
2020-10-04 12:57:14
5
2
another reason 4
2020-10-05 12:57:14
6
2
another reason 4
2020-10-06 12:57:14
Output Table
I need to pass many user Ids like in this case (1,2) and get below table only return MAX (date) per row per userId
id
userId
message
date
3
1
another reason 2
2020-10-07 14:57:14
6
2
another reason 4
2020-10-06 12:57:14
Is this possible with one Query? This what I have but not working
SELECT
id ,
userId ,
message,
date
FROM logs
WHERE userId IN (1,2)
ORDER BY date DESC;
You may use the results of a window function ROW_NUMBER to retrieve these results.
SELECT
id,userId,message,date
FROM (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY userId
ORDER BY date DESC
) as rn
FROM
logs
) t
WHERE rn=1 AND
userId IN (1,2)
ORDER BY date DESC
and for older mysql versions
SELECT
id,userId,message,date
FROM (
SELECT
l.*,
#row_num:=IF(userId=#prev_user_id,#row_num+1,1) as rn,
#prev_user_id:=userId
FROM
logs l
CROSS JOIN (
SELECT #row_num:=0, #prev_user_id:=NULL
) as vars
ORDER BY userId, date DESC
) t
WHERE rn=1 AND
userId IN (1,2)
ORDER BY date DESC

Order by date a select query on UNION ALL

I have little problem with my MySql query:
I have two tables:
Table timeline
id | date | text
1 2013-10-13 Hello
Table reps
id | date | text
1 2013-10-12 Its me again
1 2013-10-11 What?
1 2013-10-10 Lorem ipsum
What i am doing is UNION ALL timeline and reps.First row should always be the row from timeline(it's always one row) and then all rows from reps table but in DESC order.
My query is the following one(which work ok except from order by)
select id,date,text from timeline UNION ALL select * from reps order by date desc
Think something like a comment (sits on top) with replies on the comment in desc order,newest first.
Thank you in advance.
Put the UNION in a subquery:
SELECT id, date, text
FROM (SELECT id, date, text, 1 AS priority
FROM timeline
UNION ALL
SELECT *, 2 AS priority
FROM reps) u
ORDER BY priority, date DESC
your query is right , you just close second query by () like that: and it will order just the second query . without () it will order the two queries.
select id,date,text from timeline
UNION all
(select * from reps order by date desc)
Make a Unix_timestamp from the date value and order by its value.
SELECT -1 as 'id' , 99999999999999 as 'sortalias' , now() as 'datum||date'
UNION
( SELECT
id
,unix_timestamp(datum) as 'sortalias'
,datum as 'datum||date'
FROM kunde
)
order by sortalias desc
select id,date,text from (
select id,1 poid,date,text
from timeline
union all
select id,2 poid,date,text
from reps
) t
order by t.id asc,t.poid asc,t.date desc

MySQL query that selects all users that have more than one entry per day

I want to get all the USER_ID for users who have posted more than one thing per day,
I tried originally tried this
SELECT USER_ID, count(DISTINCT cast(POSTING_DATE as DATE))
AS NUM_DAYS_OF_DUPLICATES FROM POSTING_TABLE
WHERE USER_ID IN
(SELECT USER_ID FROM POSTING_TABLE
GROUP BY CAST(POSTING_DATE AS DATE) HAVING count(*) >= 2)
GROUP BY USER_ID ORDER BY NUM_DAYS_OF_DUPLICATES DESC;
Then this works for a specific USER_ID
SELECT USER_ID FROM POSTING_TABLE WHERE USER_ID = 30
GROUP BY cast(POSTING_DATE AS DATE)
HAVING count(cast(POSTING_DATE AS DATE)) > 1
The above gives me the correct result, however when I run the query on the entire table without specifying a USER_ID it does not.
eg.,
table structure USER_ID, POSTING_DATE ...
USER_ID POSTING_DATE
1 10-10-13
1 10-10-13
1 10-12-13
1 10-12-13
2 10-10-13
2 10-10-13
3 10-10-13
4 10-12-13
Where the result would give me
USER_ID NUM_DAYS_WITH_MORE_THAN_ONE_POSTING
1 2
2 1
3 0
4 0
Also if we can omit the 0's
This is the solution
select x.user_id, count(x.num_days)
from
(
select USER_ID, COUNT(USER_ID) AS NUM_DAYS
from data1
group by user_id, posting_date
having count(user_id) > 1
) x
group by 1
Working SQL Fiddle
(I used a varchar for date for simplicity but it should work fine with date too. You can check with your own database)

database query help wanted

Is it possible to limit the results a mysql select query returns with a condition?
For example I have a reviews table:
review_id, member_id, text, date
And I'd like to get the latest 10 reviews but member_id = 123 should only be taken once
Can this be achieved with a single query?
My interpretation of the problem:
the 10 most recent reviews
including at most 1 review with member_id = 123
I'm going to solve this by:
starting with the full reviews result set
removing all reviews that have member_id = 123 except for the most recent one
from the modified result set, take the 10 most recent
Here's the query:
create view newest123 as ( -- this gets the newest review for member_id 123
select *
from reviews
where member_id = 123
order by date desc limit 1
)
select *
from (
select * from newest123
union
select * -- all the reviews that aren't for member_id 123
from reviews
where member_id != 123) filtered
order by date desc limit 10 -- sort 'em and take the top 10