Count of rows grouped by date and older - mysql

I have a table that contains records for registration for an event.
I am able to get a count of registrations, grouped by the date -
SELECT DATE(`date_registered`) as `date`, COUNT(*) as `total`
FROM `registration`
GROUP BY DATE(`date_registered`)
But I would like to get the running count, based on the date -
So I am looking at doing something like -
SELECT DATE(`date_registered`) as `date`, COUNT(*) as `total`
FROM `registration`
GROUP BY DATE(`date_registered`) <= DATE(`date_registered`)
I attempted to do a LEFT JOIN with a count of all the dates prior, but that does not work -
SELECT DATE(r1.`date_registered`) as `date`, COUNT(*)+r3.preTotal as `total`
FROM `registration` r1
LEFT JOIN (
SELECT COUNT(*) as preTotal
FROM `registration` r2
WHERE DATE(r2.`date_registered`) < DATE(r1.`date_registered`)
) r3
GROUP BY DATE(r1.`date_registered`)
I have created a SQLFiddle
with the basic count - http://sqlfiddle.com/#!9/420d1d/5
and with my failed attempt - http://sqlfiddle.com/#!9/420d1d/6
I assume there is a simple way that I am missing.
Edit
Here are the same at rextester.com
basic count - http://rextester.com/GISU91151
and failed attempt - http://rextester.com/RDTFK34261

How about this:
SELECT
DATE(`date_registered`) AS date,
(
SELECT COUNT(*)
FROM `registration` t2
WHERE DATE(t2.`date_registered`) <= DATE(t1.`date_registered`)
) AS total
FROM `registration` t1
GROUP BY DATE(`date_registered`);

One way to make this work is creating a corelated subquery.
Query
SELECT
registration_counted.date
, (registration_counted.total + registration_counted.preCount) AS total
FROM (
SELECT
DATE(date_registered) AS DATE
, COUNT(*) AS total
, (
SELECT
COUNT(*)
FROM
registration AS registration2
WHERE
DATE(registration2.date_registered) < DATE(registration1.date_registered)
)
AS
preCount
FROM
registration AS registration1
GROUP BY
DATE(date_registered)
ORDER BY
DATE(date_registered) ASC
)
AS
registration_counted
Result
date total
---------- --------
2014-01-16 1
2014-01-20 2
2014-01-22 3
2014-01-31 18
2014-02-01 19
2014-02-04 22
2014-02-12 23
2014-02-19 24
2014-02-20 28
2014-02-26 30
2014-02-27 34
2014-02-28 37

Related

MySQL query for records that existed at any point each week

I have a table with created_at and deleted_at timestamps. I need to know, for each week, how many records existed at any point that week:
week
records
2022-01
4
2022-02
5
...
...
Essentially, records that were created before the end of the week and deleted after the beginning of the week.
I've tried various variations of the following but it's under-reporting and I can't work out why:
SELECT
DATE_FORMAT(created_at, '%Y-%U') AS week,
COUNT(*)
FROM records
WHERE
deleted_at > DATE_SUB(deleted_at, INTERVAL (WEEKDAY(deleted_at)+1) DAY)
AND created_at < DATE_ADD(created_at, INTERVAL 7 - WEEKDAY(created_at) DAY)
GROUP BY week
ORDER BY week
Any help would be massively appreciated!
I would create a table wktable that looks like so (for the last 5 weeks of last year):
yrweek | wkstart | wkstart
-------+------------+------------
202249 | 2022-11-27 | 2022-12-03
202250 | 2022-12-04 | 2022-12-10
202251 | 2022-12-11 | 2022-12-17
202252 | 2022-12-18 | 2022-12-24
202253 | 2022-12-25 | 2022-12-31
To get there, find a way to create 365 consecutive integers, make all the dates of 2022 out of that, and group them by year-week.
This is an example:
CREATE TABLE wk AS
WITH units(units) AS (
SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION
SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
)
,tens AS(SELECT units * 10 AS tens FROM units )
,hundreds AS(SELECT tens * 10 AS hundreds FROM tens )
,
i(i) AS (
SELECT hundreds +tens +units
FROM units
CROSS JOIN tens
CROSS JOIN hundreds
)
,
dt(dt) AS (
SELECT
DATE_ADD(DATE '2022-01-01', INTERVAL i DAY)
FROM i
WHERE i < 365
)
SELECT
YEAR(dt)*100 + WEEK(dt) AS yrweek
, MIN(dt) AS wkstart
, MAX(dt) AS wkend
FROM dt
GROUP BY yrweek
ORDER BY yrweek;
With that table, go:
SELECT
yrweek
, COUNT(*) AS records
FROM wk
JOIN input_table ON wk.wkstart < input_table.deleted_at
AND wk.wkend > input_table.created_at
GROUP BY
yrweek
;
I first build a list with the records, their open count, and the closed count
SELECT
created_at,
deleted_at,
(SELECT COUNT(*)
from records r2
where r2.created_at <= r1.created_at ) as new,
(SELECT COUNT(*)
from records r2
where r2.deleted_at <= r1.created_at) as closed
FROM records r1
ORDER BY r1.created_at;
After that it's just adding a GROUP BY:
SELECT
date_format(created_at,'%Y-%U') as week,
MAX((SELECT COUNT(*)
from records r2
where r2.created_at <= r1.created_at )) as new,
MAX((SELECT COUNT(*)
from records r2
where r2.deleted_at <= r1.created_at)) as closed
FROM records r1
GROUP BY week
ORDER BY week;
see: DBFIDDLE
NOTE: Because I use random times, the results will change when re-run. A sample output is:
week
new
closed
2022-00
31
0
2022-01
298
64
2022-02
570
212
2022-03
800
421

Find MySql concurrent user per hour in a week

This is my table, i want to find concurrent user per hour for a given week
I am trying to calculate number of concurrent users in a time range. The input looks something like the below
Table
id user_id login_time
1 23 2016-06-08 09:10:00
2 24 2016-06-08 08:55:00
3 25 2016-06-08 09:29:00
4 26 2016-06-08 09:40:00
5 27 2016-06-08 09:08:00
6 28 2016-06-09 13:40:00
7 31 2016-06-09 14:04:00
How to get the concurrent users in time range ?
Expected Output Table
Date
Hour
User
2014-08-04
0
3
2014-08-04
1
2
2014-08-04
2
0
2014-08-05
0
1
Similar question
concurrent users sql
I created a DBFIDDLE
first I entered the data from your question
half-way I changed data to what was given here: http://sqlfiddle.com/#!9/67356f/2
first the cte1 contains the first and last date from users.
cte2 contains all the dates between StartDate and EndDate
cte3 contains all (24) hours for the dates.
After this is is just counting to see if a user is logged in.
WITH RECURSIVE cte1 AS (
SELECT
DATE(MIN(login_time)) StartDate,
DATE(MAX(login_time)) EndDate
FROm users),
cte2 AS (
SELECT cte1.StartDate
from cte1
union all
select DATE_ADD(cte2.StartDate, INTERVAL 1 DAY)
from cte2
cross join cte1 where cte2.StartDate < cte1.EndDate
),
cte3 AS (
SELECT StartDate, 0 as H
FROM cte2
UNION ALL
SELECT StartDate, H+1 FROM cte3 WHERE H<24
)
select * from (
select
StartDate as `Date`,
H as `hour`,
(SELECT count(*) from users
WHERE login_time BETWEEN DATE_ADD(StartDate, interval H HOUR) AND DATE_ADD(StartDate, interval (H+1) HOUR)
) as `Count`
from cte3) x
where x.`Count` <>0
order by 1,2;
You can begin with this, but (from my opinion) it has no sense the result you are trying to get because you need to calculate the time:
If a user enters 9:30 and left 9:35 and re-enter 9:45 is not a concurrent user but you get this in the SQL.
If a user enters 9:59 and enter 10:01 you have a concurrent user but you won't see this with this logic of "hour"
Concurrent user with different day (23:59 and 00:01 logins)
In any case, the SQL you are asking for:
SQL Fiddle
SELECT
up.user_id,
up.diff as TimeDiff,
FROM
(
SELECT TIMESTAMPDIFF(HOUR,u1.login,u2.login) as diff, u1.user_id FROM users u1
JOIN users u2
ON u1.user_id = u2.user_id
AND u1.login < u2.login ) up
WHERE up.diff < 1
And without DIFF time (as you requested):
SELECT
g.id,
g.hour,
g.datelogin,
COUNT(*) as times
FROM
(SELECT HOUR(login) as hour, DATE(login) as datelogin, id FROM users) g
GROUP BY datelogin, hour, id
HAVING COUNT(*) > 1 -- This will show only counts is bigger than 1

Write Query to display look like in image

The table provided shows all new users signing up on a specific date in the format YYYY-MM-DD.
Your query should output the change from one month to the next. Because the first month has no preceding month, your output should skip that row. Your output should look like the following table.
My table data
Table data:
ID DateJoined
1 2017-01-06
2 2017-01-12
3 2017-01-16
4 2017-01-25
5 2017-02-05
6 2017-02-07
7 2017-02-21
8 2017-03-05
9 2017-03-07
10 2017-03-14
11 2017-03-16
12 2017-03-25
13 2017-03-25
14 2017-03-25
15 2017-03-25
16 2017-03-26
17 2017-04-05
18 2017-04-14
19 2017-04-21
20 2017-05-07
23 2017-05-14
24 2017-05-16
25 2017-05-25
26 2017-05-25
27 2017-05-25
28 2017-05-25
Enter image description here
I want this output:
count all records from every month and subtract it from the next month record.
This is my query:
SELECT
MONTH(L.joindate),
COUNT(L.joindate) - COUNT(R.joindate),
MONTH(R.joindate),
COUNT(R.joindate)
FROM
userlog AS L
LEFT JOIN
userlog AS R
ON MONTH(R.joindate)= (SELECT MIN(MONTH(joindate)) FROM userlog WHERE MONTH(joindate) < MONTH(L.joindate))
GROUP BY (MONTH(L.joindate)),(MONTH(R.joindate));
Use lag(), available in MySQL 8.0:
select date_format(joindate, '%Y-%m-01') joinmonth,
count(*) - lag(count(*), 1, 0) over(order by date_format(joindate, '%Y-%m-01')) m2m
from userlog
group by joinmonth
Note that I changed the logic to truncate dates to the first of month to use date_format().
In earlier versions, you can use a correlated subquery:
select date_format(joindate, '%Y-%m-01') joinmonth,
count(*) - (
select count(*)
from userlog l1
where l1.joindate >= date_format(l.joindate, '%Y-%m-01') - interval 1 month
and l1.joindate < date_format(l.joindate, '%Y-%m-01')
) m2m
from userlog l
group by joinmonth
LIMIT 12 OFFSET 1
You need to use Lag. Also, since it says you need to skip the first row so I have used the not null condition. I believe this query should work.
select
Month,
MonthToMonthChange
from
(
select
m_name as Month,
(total_id - diff) as MonthToMonthChange
from
(
select
total_id,
m_name,
Lag(total_id, 1) OVER(
ORDER BY
m_num ASC
) AS diff
from
(
select
MonthNAME(DateJoined) m_name,
Month(DateJoined) m_num,
count(*) total_id
from
maintable
Group by
m_name,
m_num
) as first_subquery
) as second_subquery
) as final_query
where
MonthToMonthChange IS NOT NULL;
select
MONTHNAME(UL1.DateJoined) as MONTH,
count(UL1.DateJoined) -
(
select count(UL2.DateJoined)
from tablename UL2
where MONTH(UL2.DateJoined )=MONTH(UL1.DateJoined) -1
) as MonthToMonthChange
from tablename UL1
where Month(UL1.DateJoined)!=1
Group by MONTHNAME(UL1.DateJoined)
order by UL1.DateJoined ASC;
https://i.stack.imgur.com/BXXDb.png
I tried this and it worked
select date_format(DateJoined, CONCAT('%M')) as Month,
count(*) - lag(count(*), 1, 0) over(order by date_format(DateJoined, CONCAT('%m'))) MonthToMonthChange
from maintable_OKLOT
group by Month
limit 12 offset 1

MySQL function IFNULL not working with GROUP BY

I've got a standard table with the list of users and I've got a column lastactivity with UNIX Timestamp (which shows when they have logged in) and column timestamp with UNIX Timestamp that shows when they have registered.
I've build a SQL query that shows how many users were active within 24 hours (86400 seconds) from now and grouped results by weeks so the counter counts how many users have registered each week:
SELECT
IFNULL(COUNT(*),0) as `counter`,
(WEEK(`timestamp`)) as `week`
FROM
`clients`
WHERE
(CAST(UNIX_TIMESTAMP() as signed) - CAST(`lastactivity` as signed)) <= 86400
GROUP BY
WEEK(`timestamp`);
The issue is that function IFNULL(COUNT(*),0) is not working as I intended. This SQL query won't display the week if there is NULL / 0 on the counter even with IFNULL() MySQL function. That is probably because of how GROUP BY works. So for example I will get this kind of result:
counter | week
2 | 11
1 | 13
9 | 14
6 | 17
But I would like to show each week like this:
counter | week
2 | 11
0 | 12
1 | 13
9 | 14
0 | 15
0 | 16
6 | 17
Anyone have idea how can I fix this issue?
Gordon is trying to help me by getting LEFT JOIN query but I still got the same results, maybe I am doing something wrong here:
SELECT
COUNT(a.id) as `counter`,
(WEEK(b.timestamp)) as `week`
FROM
`users` a
LEFT JOIN
`users` b
ON
a.id = b.id
WHERE
(CAST(UNIX_TIMESTAMP() as signed) - CAST(a.lastactivity as signed)) <= 86400
GROUP BY
WEEK(b.timestamp);
The problem is that you don't understand how the query works. IFNULL() (or the standard version COALESCE() converts a column value that is NULL to some other value. However, COUNT() never returns NULL. So, leave it out:
SELECT COUNT(*) as `counter`, WEEK(`timestamp`) as `week`
FROM `clients`
WHERE (CAST(UNIX_TIMESTAMP() as signed) - CAST(`lastactivity` as signed)) <= 86400
GROUP BY WEEK(`timestamp`);
Your problem is missing rows, not NULL values. You would have to solve this with a LEFT JOIN.
EDIT:
You need a left join to include all the weeks:
SELECT COUNT(c.timestamp) as `counter`, wk as `week`
FROM (SELECT 11 as wk UNION ALL
SELECT 12 UNION ALL
SELECT 13 UNION ALL
SELECT 14 UNION ALL
SELECT 15 UNION ALL
SELECT 16 UNION ALL
SELECT 17
) w LEFT JOIN
`clients` c
ON WEEK(c.`timestamp`) = w.wk
WHERE (CAST(UNIX_TIMESTAMP() as signed) - CAST(`lastactivity` as signed)) <= 86400
GROUP BY WEEK(`timestamp`);

MySQL query to return MIN() and MAX() of different columns for multiple rows

I currently have a prices table with the following layout:
id codename price discount timestamp
1 1234 599 50 2015-06-10 00:00:00
2 1234 1099 25 2015-06-11 00:00:00
3 3344 199 33 2015-06-12 00:00:00
4 5565 2499 0 2015-06-13 00:00:00
5 5565 1299 50 2015-06-14 00:00:00
I need an SQL query that will give me a single row for each codename. Each row must contain the codename, then the lowest price (along with the associated discount and timestamp for that price), as well as the latest timestamp (again with the associated price and discount for that timestamp)
Desired output:
codename minTimePrice minTimeDis minTime latestPrice latestPriceDis latestPriceTime
1234 599 50 2015-06-10 00:00:00 1099 25 2015-06-11 00:00:00
3344 199 33 2015-06-12 00:00:00 199 33 2015-06-12 00:00:00
5565 1299 50 2015-06-14 00:00:00 1299 50 2015-06-14 00:00:00
EDIT: So I have gotten to where I can have the 2 seperate queries, one gets the row with the MIN(price) and the second gets the row with the MAX(timestamp) for each codename.
Now what I need to do is join them together so that they are all on the same row (grouped by codename) as in the example above.
SQL Fiddle of 2 queries
So after some playing with joins I was able to get the 2 queries to output onto a single row per codename:
SELECT *
FROM
(
SELECT p.*
FROM prices p
JOIN
(
SELECT codename, MIN(price) minPrice
FROM prices GROUP BY codename
) p2
ON p.price = p2.minPrice AND p.codename = p2.codename
) min
LEFT JOIN
(
SELECT p.*
FROM prices p
JOIN
(
SELECT codename, MAX(timestamp) maxTime
FROM prices GROUP BY codename
) p2
ON p.timestamp = p2.maxTime AND p.codename = p2.codename
) latest
ON latest.codename = min.codename
I'm sure the query is far from perfect, but it does give me the results I am looking for.
SQL Fiddle
If there is anything drastically wrong with this, please let me know and I can update.
Try following,
Select codename, minPrice, minDis, minTime, latestPrice, latestDis, latestTime from
(
Select T_Low.codename, minPrice, minDis, minTime, T_Latest.latestPrice, T_Latest.latestDis, T_Latest.latestTime from
(
select * from (
select row_number() over(partition by codename order by codename, price) row_id, codename, price as minPrice, discount as minDis, timestamp as minTime from
(
select codename, discount, timestamp , min(price) as price from prices
group by codename, discount, timestamp
)T
) T1
where row_id = 1
) T_Low
left join
(
select * from (
select row_number() over(partition by codename order by codename, timestamp desc) row_id, codename, price as latestPrice, discount as latestDis, timestamp as latestTime from
(
select codename, discount, timestamp , min(price) as price from prices
group by codename, discount, timestamp
)T
) T1
where row_id = 1
)t_Latest
ON T_Low.codename= T_Latest.codename and T_Low.row_id = T_Latest.row_id
)T
order by codename