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
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.
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
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
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
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