MySQL order results by datetime and grouped by user ID - mysql

I have a table that looks like this:
user_id
datetime
activity
2
2022-01-10 12:00:00
Logout
1
2022-01-09 12:00:00
Login
3
2022-01-08 12:00:00
Login
3
2022-01-07 12:00:00
Register
2
2022-01-06 12:00:00
Login
1
2022-01-05 12:00:00
Register
If I query the table sorted by datetime DESC I will get the result like the above.
How can I extend the query so that I can get the results grouped by the user_id like below?
user_id
datetime
activity
2
2022-01-10 12:00:00
Logout
2
2022-01-06 12:00:00
Login
1
2022-01-09 12:00:00
Login
1
2022-01-05 12:00:00
Register
3
2022-01-08 12:00:00
Login
3
2022-01-07 12:00:00
Register
The logic is the records will be sorted by datetime DESC at first and when it encounters the user_id for the record, it will aggregate all records belonging to the user_id together and maintaining the datetime DESC sorting within the user_id group.

Use MAX() window function in the ORDER BY clause:
SELECT *
FROM tablename
ORDER BY MAX(datetime) OVER (PARTITION BY user_id) DESC,
user_id, -- just in case there are duplicate datetimes, remove this if the column datetime is unique
datetime DESC;
See the demo.

Related

Counting club memberships in SQL

EDIT: I have added the primary key, following the comment by #Strawberry
The aim is to return the number of current members, and also the number of past memberships, on any particular date/time.
For example, suppose we have
msid id start cancelled
1 1 2020-01-01 09:00:00 null
2 2 2020-01-01 09:00:00 2020-12-31 09:00:00
3 2 2021-01-01 09:00:00 null
4 3 2020-01-01 09:00:00 2020-06-30 09:00:00
5 3 2020-02-01 09:00:00 2020-06-30 09:00:00
6 3 2020-07-01 09:00:00 null
and we want to calculate the number of members at various times, which should return as follows
Datetime Current Past <Notes - not to be returned by the query>
2020-01-01 12:00:00 3 0 -- all 3 IDs have joined earlier on this date
2020-02-01 12:00:00 3 0 -- new membership for existing member (ID 3) is not counted
2020-06-30 12:00:00 2 1 -- ID 3 has cancelled earlier on this day
2020-07-01 12:00:00 3 0 -- ID 3 has re-joined earlier on this day
2020-12-31 12:00:00 2 1 -- ID 2 has cancelled earlier on this day
2021-01-01 12:00:00 3 0 -- ID 2 has re-joined earlier on this day
An ID may either be current or past, but never both. That is, if a past member re-joins, as in the case of ID 2 and 3 above, they become current members, and are no longer past members.
Also, a member may have multiple current memberships, but they can only be counted as a current member once, as in the case of ID 3 above.
How can this be achieved in MySQL ?
Here is a db<>fiddle with the above data
Test this:
WITH
cte1 AS ( SELECT start `timestamp` FROM dt
UNION
SELECT cancelled FROM dt WHERE cancelled IS NOT NULL ),
cte2 AS ( SELECT DISTINCT id
FROM dt )
SELECT cte1.`timestamp`, COUNT(DISTINCT dt.id) current, SUM(dt.id IS NULL) past
FROM cte1
CROSS JOIN cte2
LEFT JOIN dt ON cte1.`timestamp` >= dt.start
AND (cte1.`timestamp` < dt.cancelled OR dt.cancelled IS NULL)
AND cte2.id = dt.id
GROUP BY cte1.`timestamp`
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=942e4c97951ed0929e178134ef67ce69

Recursively calculate records within a time interval in mysql

I have a table where records will be getting inserted every 4 hours on a daily basis. If the record was not inserted for continuous 4 hours, I need to insert a log into another table. Below is the table schema.
Id DocPathid CreatedAt
1 1 2021-04-02 00:00:00
2 1 2021-04-02 04:00:00
3 1 2021-04-02 09:00:00
4 1 2021-04-02 12:00:00
5 1 2021-04-02 16:00:00
6 1 2021-04-02 20:00:00
7 1 2021-04-02 24:00:00
In the above case, there was no records inserted within a interval of 4hours (i.e. between 2021-04-02 04:00:00 & 2021-04-02 09:00:00). The query should return no. of failure count (in this case it is failed for 1 time).
Is there a way to achieve this in MySQL?
You can do something like this.
select count(1)
from (
select id, CreatedAt , timestampdiff(hour, CreatedAt
, lead(CreatedAt,1) over (partition by DocPathid order by CreatedAt) ) as hour
from Table1
) t
where hour >4
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=9b0c631145422dbccd2ea23f0a7d2011

SQL Insert end of day for user last activity

I have this table
user_id activity dt
1 login 2020-01-01 08:00:00
1 logout 2020-01-01 20:00:00
2 home 2020-01-01 19:00:00
1 profile 2020-01-02 08:00:00
I need to insert last day for every user and the event of the last day is the last activity of that user. for example if user A logout on 2020-01-01 20:00:00 then you insert user A logout on 2020-01-02 00:00:00. the example is like in the last 3 rows of this table:
user_id activity dt
1 login 2020-01-01 08:00:00
1 logout 2020-01-01 20:00:00
2 home 2020-01-01 19:00:00
1 profile 2020-01-02 08:00:00
1 logout 2020-01-02 00:00:00
2 home 2020-01-02 00:00:00
1 profile 2020-01-03 00:00:00
on 2020-01-01 there is 2 user that have activity, so you have to input last activity of user 1 and 2 and the time is 2020-01-02 00:00:00.
I already search this solution on internet but can't find out the way to do it. All I have done is insert it manual one by one
In modern MySQL since version 8.0 you can use next approach:
insert into tbl
select distinct
user_id,
last_value(activity) over (partition by user_id, date(dt)),
date_add(date(dt), interval 1 day) dt
from tbl
order by dt
;
share SQL query
Hmmm . . . To get the last activity on each date with the appropriate new dt column:
select user_id, activity,
date(dt) + interval 1 day
from (select t.*,
row_number() over (partition by user_id, date(dt) order by dt desc) as seqnum
from t
) t
where seqnum = 1;
For a result set, you can union this to the existing table. If you want to actually modify the table, then insert these rows into the table.

How to compare date fields of different fields?

id ref_id dates
1 1 2017-01-01 00:00:00
2 1 2017-01-31 00:00:00
3 2 2017-01-01 00:00:00
4 2 2017-01-31 00:00:00
5 3 2016-01-01 00:00:00
6 3 2016-01-31 00:00:00
Query will be like
SELECT * FROM table WHERE dates GROUP By ref_id
I would like to compare ref_id wise Start Date in 1st row & End Date in 2nd row should be between Current Date.
So output will be only those ref_id which are in between current date.
Output
ref_id
1
2
or GROUP_CONCAT of that 1,2
Would like to using single query without UNION.
Try this:
SELECT ref_id
FROM table
GROUP BY ref_id
HAVING NOW() BETWEEN MIN(dates) AND MAX(dates)
The query uses NOW in order to get the current date/time value. If this value lies within the interval defined by the minimum and maximum date value of a ref_id group, then this ref_id is returned by the query.

mysql: how to limit to query?

I select records and group them by date and uid(which is not the primary key), and I need to page those records by date, how can I do it?
For example:
uid date
1 2012-01-10
2 2012-01-10
3 2012-01-09
3 2012-01-09
3 2012-01-11
sql:
SELECT date, uid
FROM users
GROUP by date,`uid`
Results:
uid date
1 2012-01-10
2 2012-01-10
3 2012-01-09
3 2012-01-11
Because I need to page those record by date, if I use sql like:
SELECT date, uid
FROM users
GROUP by date,`uid`
LIMIT 0,2
then I just get the records like this:
uid date
1 2012-01-10
2 2012-01-10
. how can I page the record by date.
The results I want when page size is 2:
uid date
1 2012-01-10
2 2012-01-10
3 2012-01-09
SELECT date, uid
FROM users
WHERE date >= 'your date'
AND date < 'your date' LIMIT 0, 3