Finding rows with latest date by id mysql - 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

Related

Get last distinct value from 2 columns

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

Select the first row after order by using distinct value

I have a table named emails which looks like this:
customer_id
email_template_id
send_time
order_type
1
1
2021-01-10
1
2
1
2021-01-10
1
1
2
2021-02-10
2
3
1
2021-03-10
1
2
2
2021-03-10
2
1
3
2021-04-10
1
I want to order the data by email_template_id which will be given on the web page (can be 1,2,3...). Whenever a person clicks on sort by (template_id = 2), I want to retrieve the data order by template_id then by date, but still get the unique customer_id of course. The result should look something like this:
customer_id
email_template_id
send_time
order_type
1
2
2021-02-10
2
2
2
2021-03-10
2
3
1
2021-03-10
1
I have tried this but I am getting duplicate rows.
select distinct customer_id as cust,order_type,email_template_id,send_time
from email_order
ORDER by FIELD(email_template_id,2,1,3,4,5,6),send_time desc
I tried using subquery to select distinct customer_id
select distinct(t1.customer_id) from
(select distinct customer_id as cust,email_template_id
from email_order
ORDER by FIELD(email_template_id,2,1,3),send_time desc) as t1
It works when I only select (t1.customer_id), when ever I select order_type or send_time from t1, it no longer shows unique customer_id, but shows the data according to send_time desc etc
Your help and time will be highly appreciated!
If your have an older version you can use the query below:
select customer_id, email_template_id, send_time, order_type
from ( select customer_id, email_template_id, send_time,order_type
from emails order by case when email_template_id=2 then -1 else email_template_id end ) as e
group by customer_id;
SQLfiddle working on MySQL 5.6 : http://sqlfiddle.com/#!9/d55f4d/1
But if the version is MariaDB, i think not in your case you should apply limit inside the subquery.
The images below are example in 10.4.17-MariaDB version
ROW_NUMBER should work well here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY customer_id
ORDER BY FIELD(email_template_id, 2, 1, 3, 4, 5, 6)) rn
FROM email_order
)
SELECT customer_id, email_template_id, send_time, order_type
FROM cte
WHERE rn = 1;

MySQL group multiple rows based on DISTINCT value

I need to display the last 2 results from a table (results), the results are comprised of several rows with matching submissionId, The number of rows per submission is unknown, and of course I prefer a single query.
Here is the DB table structure
submissionId input value
1 name jay
1 phone 123-4567
1 email test#gmail.com
2 name mo
2 age 32
3 name abe
3 email abe#gmail.com
4 name jack
4 phone 123-4567
4 email jack#gmail.com
Desierd results:
submissionId input value
3 name abe
3 email abe#gmail.com
4 name jack
4 phone 123-4567
4 email jack#gmail.com
Or even better, if I can combine the rows like this:
3 name abe 3 email abe#gmail.com
4 name jack 4 phone 123-4567 4 email jack#gmail.com
One option here is to use a subquery to identify the most recent and next to most recent submissionId:
SELECT submissionId, input, value
FROM yourTable
WHERE submissionId >= (SELECT MAX(submissionId) FROM yourTable) - 1
ORDER BY submissionId
Demo here:
SQLFiddle
Update:
If your submissionId column were really a date type, and you wanted the most recent two dates in your result set, then the following query will achieve that. Note that the subquery in the WHERE clause, while ugly, is not correlated to the outer query. This means that the MySQL optimizer should be able to figure out that it only needs to run it once.
SELECT submissionDate, input, value
FROM yourTable
WHERE submissionDate >=
(SELECT MAX(CASE WHEN submissionDate = (SELECT MAX(submissionDate) FROM yourTable)
THEN '1000-01-01'
ELSE submissionDate
END) FROM yourTable)
ORDER BY submissionDate
SQLFiddle
You can use limit in subqueries in the from clause, so a typical way to write this is:
SELECT submissionDate, input, value
FROM t join
(select distinct submissionDate
from t
order by submissionDate desc
limit 2
) sd
on t.submissionDate = sd.submissionDate;
This is how the query looks like now, so i can get the results with a LIMIT, RANGE, and id/timestamp (with help of Tim and Gordon):
SELECT *
FROM rmyTable t
JOIN
(SELECT DISTINCT sd.submissionId
FROM myTable sd
WHERE sd.questionId = yourId
ORDER BY sd.submissionId
LIMIT 2
) t2
ON t.submissionId = t2.submissionId
WHERE t.formId = yourId
AND dateTime BETWEEN 0000 AND 1111

Sorted data in groups

Dataset:
id uid activity postid
1 20 A 1
2 20 A 1
3 6 A 1
4 3 A 1
5 6 A 1
6 13 A 1
7 13 B 1
8 18 B 1
9 18 B 1
10 1 A 1
Current Results:
id uid uid_list groupCount activity postid
9 18 18,13 3 B 1
1 20 1,13,6,3,20 7 A 1
Expected Results:
id uid uid_list groupCount activity postid
9 18 18,13 3 B 1
10 1 1,13,6,3,20 7 A 1
The query I have:
SELECT
id,
uid,
GROUP_CONCAT(DISTINCT uid ORDER BY id DESC) as uid_list,
COUNT(*) as groupCount,
activity,
postid
FROM (
SELECT *
FROM `user_activities`
ORDER BY id DESC) as tbl
GROUP BY
activity, postid
ORDER BY
id DESC
I want to group by activity and postid while having the result in descending order by id. And want to have the latest id and uid for every group. I don't understand why this query doesn't return the expected output.
From what I understand id value is increasing. To get the latest values you could use an aggregate function MAX().
Also, your inner query with ordering is unnecessary because the engine has to sort the resultset by id anyways when building result for GROUP_CONCAT().
To retrieve uid for a particular id column you need to self join to the same table.
SELECT
a.id, b.uid, a.uid_list, a.groupcount, a.activity, a.postid
FROM (
SELECT
MAX(id) as id,
GROUP_CONCAT(DISTINCT uid ORDER BY id DESC) as uid_list,
COUNT(*) as groupCount,
activity,
postid
FROM user_activities a
GROUP BY
activity, postid
) a
INNER JOIN user_activities b ON a.id = b.id
Probably the simplest method is the group_concat()/substring_index() trick:
SELECT MAX(ID) as id,
SUBSTRING_INDEX(GROUP_CONCAT(uid ORDER BY ID DESC), ',', 1) as uid,
GROUP_CONCAT(DISTINCT uid ORDER BY id DESC) as uid_list,
COUNT(*) as groupCount,
activity, postid
FROM user_activities ua
GROUP BY activity, postid
ORDER BY id DESC;
There are some limitations to this approach, in the sense that GROUP_CONCAT() has a maximum length for the intermediate value. Typically the default is sufficient, but you might need to change that value if many, many rows match each group (and you already have this issue for the list of uids anyway).

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)