SQL Insert end of day for user last activity - mysql

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.

Related

MySQL order results by datetime and grouped by user ID

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.

Calculate Employee Experience from Multiple Jobs

I have a table named employee experience with id, userId, startDate, endDate columns.
I want to calculate employee experience. Can someone please help with mysql query or JPA specification code?
For example in case of following data:
id
userID
startDate
endDate
1
1
2021-01-01
2022-01-01
2
2
2019-01-01
2020-01-01
3
2
2020-01-02
2021-01-01
4
3
2021-01-01
2022-01-01
the output should be:
userID
experience
1
1
2
2
3
1
Successfully did this with the following:
SELECT SUM(TIMESTAMPDIFF(YEAR, START_DATE, END_DATE)) AS experience,
SOCIAL_PROFILE_ID
FROM tableName
GROUP BY SOCIAL_PROFILE_ID

Loading multiple time series simultaneously using SQL

Suppose I have this exact dataset:
date
widget ID
widget price
widget expiry date
2020-01-01
A
1
2020-03-01
2020-01-01
B
2
2020-04-01
2020-01-01
C
3
2020-05-01
2020-01-01
D
4
2020-06-01
2020-01-02
A
1.1
2020-03-01
2020-01-02
B
2.05
2020-04-01
2020-01-02
C
3.7
2020-05-01
2020-01-02
D
3.8
2020-06-01
2020-01-03
A
1.15
2020-03-01
2020-01-03
B
2.09
2020-04-01
2020-01-03
C
3.54
2020-05-01
2020-01-03
D
4.2
2020-06-01
2020-01-04
A
1.19
2020-03-01
2020-01-04
B
2.14
2020-04-01
2020-01-04
C
3.73
2020-05-01
2020-01-04
D
4.30
2020-06-01
Say I wanted to simultaneously retrieve the full time series of the two following widgets using a single SQL query:
the widget which on date 2020-01-01 had price as close as possible to 1 and expiry date as close as possible to 2020-03-10.
the widget which on date 2020-01-03 had price as close as possible to 3.5 and expiry date as close as possible to 2020-05-15.
In other words, this exact table:
date
widget ID
widget price
widget expiry date
2020-01-01
A
1
2020-03-01
2020-01-01
C
3
2020-05-01
2020-01-02
A
1.1
2020-03-01
2020-01-02
C
3.7
2020-05-01
2020-01-03
A
1.15
2020-03-01
2020-01-03
C
3.54
2020-05-01
2020-01-04
A
1.19
2020-03-01
2020-01-04
C
3.73
2020-05-01
How would you recommend going about it?
Generalising this example, suppose you had a list of tuples like below, where price_i is a target price and expiry_date_i is a target expiry date.
(date_1, price_1, expiry_date_1), (date_2, price_2, expiry_date_2),
(date_3, price_3, expiry_date_3),...
How would you load all of the corresponding widgets' time series in one go?
For the time being I am retrieving these widgets' IDs separately using a SQL query like this one (in this example date='2020-01-01', price=1, expiry date='2020-03-10'). Then collecting all of these retrieved IDs I load the full widget time series.
WITH sample AS
(SELECT *, ABS(DATEDIFF(day,widget_expiry_date, '2020-03-10')) AS date_diff, ABS(widget_price - 1) As price_diff
FROM data WHERE date='2020-01-01'
ORDER BY date_diff ASC, price_diff ASC)
SELECT TOP 1 widget_ID FROM sample
As you can imagine this is extremely inefficient. I wonder if there is a smarter way about it?
Thank you for your time and apologies in advance for the noobish question.
Retrieving all the series in a single query
with params (date_, price_, expiry_date_) AS (
select date '2020-01-01', 1, date '2020-03-10' union all
select date '2020-01-03', 3.5, date '2020-05-15'
)
select data.*
from params p
join data on data.widgetID = (
SELECT widgetID
FROM data d
WHERE d.date = p.date_
ORDER BY ABS(DATEDIFF(d.widget_expiry_date, p.expiry_date_)) ASC, ABS(d.widget_price - p.price_) ASC
LIMIT 1);
db<>fiddle
you also can use window functions:
SELECT indate , widgetID , price , expirydate FROM (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY indate ORDER BY ABS(price - 1), ABS(DATEDIFF(expirydate, '2020-03-10')) ) rn1
, ROW_NUMBER() OVER (PARTITION BY indate ORDER BY ABS(price - 3.5), ABS(DATEDIFF(expirydate, '2020-05-15')) ) rn2
FROM widgets
) t
WHERE rn1 =1 OR rn2 = 1
ORDER BY indate , widgetID
db<>fiddle here

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