Counting club memberships in SQL - mysql

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

Related

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.

MYSQL return zero in date not present, and COUNT how many rows are present with a specific date

I have a table named calendar with a single column (mydate DATE).
MYDATE
2020-01-01
2020-01-02
2020-01-03
...
I have also a table named delivery with three columns (id PK, giorno DATE, totale FLOAT)
ID GIORNO TOTALE
1 2020-01-01 0.10
2 2020-01-01 5
3 2020-01-02 12
4 2020-01-12 5
5 2020-02-02 13.50
This is what I'm trying to obtain:
Day Numbers of orders
2020-01-01 2
2020-01-02 1
2020-01-03 0
2020-01-04 0
2020-01-05 0
2020-01-06 0
2020-01-07 0
2020-01-08 0
2020-01-09 0
2020-01-10 0
2020-01-11 0
2020-01-12 1
...
I was trying this query:
SELECT c.mydate, IFNULL(d.totale, 0) value
FROM delivery d
RIGHT JOIN calendar c
ON ( c.mydate = d.giorno )
GROUP BY c.mydate
ORDER BY c.mydate
Consider:
select c.mydate, count(d.id) number_of_orders
from calendar c
left join delivery d on d.giorno = c.mydate
group by c.mydate
This works by left-joining the calendar table with the orders table, then aggregating by date, and finally counting the number of matching rows in the order table.
This is quite close to your original query (although this uses left join instead of right join), however this uses an aggregate function to count the orders.

Mysql finding count of previous date of occurences for each record

I have a table to store id, sid with a date time.
id is used as primary key and no meaning in data.
sid is used to identify entity.
eg.
id sid date
--------------------
1 1 2020-01-12
2 2 2020-01-01
3 1 2019-12-31
4 2 2019-12-31
5 1 2019-12-31
6 1 2019-11-01
7 3 2019-11-01
8 3 2018-12-21
9 2 2018-12-21
Then I would like to query for each record, count occurrences in the same table with the previous date of current date, and with the same sid, like:
id sid date previous_count
----------------------------------
1 1 2020-01-12 2
2 2 2020-01-01 1
3 1 2019-12-31 1
4 2 2019-12-31 1
5 1 2019-12-31 1
6 1 2019-11-01 0
7 3 2019-11-01 1
8 3 2018-12-21 0
9 2 2018-12-21 0
Explanation:
for row 1, since sid 1 has two records in 2019-12-31, which is the previous date of 2020-01-12 for sid 1 in the table, it has 2 in previous_count;
while in row 2, since sid 2 has only 1 record in 2019-12-31, which is the previous date of 2020-01-01 for sid 2, it has 1 in previous_count.
Thanks
Your are looking for dense_rank() - 1:
select t.*,
(dense_rank() over (partition by sid order by date) - 1) as previous_count
from t
order by id;
In older versions of MySQL, you could use variables or a correlated subquery:
select t.*,
(select count(distinct t2.date)
from t t2
where t2.sid = t.sid and t2.date < t.date
) as previous_count
from t
order by id;
EDIT:
Ahh, I think I may have misunderstood the problem. I think this does what you want:
select t.*, lag(cnt, 1, 0) over (partition by sid order by date)
from (select t.*,
count(*) over (partition by sid, date) as cnt
from t
) t
order by id;
Here is a db<>fiddle.

SQL joining on two columns with a variation on the first column

I have two tables:
Table 1: planA
ID Date Count
3 2017-01-01 10
2 2017-02-03 15
10 2017-01-30 8
Table 2: planB
ID Date Value
3 2017-01-02 11
2 2017-02-04 12
21 2017-01-30 3
3 2017-02-03 33
What I want to do is to join the two tables on (ID and Date) columns.
However, on Date, I want to use the next day to the date on the table 1.
Therefore, the joined table should look like the following:
PlanA.ID PlanA.Date PlanB.Date PlanA.Count PlanB.Value
3 2017-01-01 2017-01-02 10 11
2 2017-02-03 2017-02-04 15 12
Is this even possible?
Any suggestion would be appreciated!
Yes it is possible:
select
PlanA.ID,
PlanA.Date,
PlanB.Date,
PlanA.Count,
PlanB.Value
from
PlanA inner join PlanB
on (
PlanA.ID = PlanB.ID
and
PlanA.Date + INTERVAL 1 DAY = PlanB.Date
)
if Date is a column of type date, + INTERVAL 1 DAY will return the next day of the one given, and then you can perform the join.