Need some modification in my SQL query - mysql

I have a query which returns total number of users registered on a particular days in 7 days interval time. I also want to get the zero count data so i can plot it on my graph. How to fetch zero count values?
The query :
select date(update_timestamp) as date, count(*) users
from registered
where date(update_timestamp) between date_sub(sysdate(), interval 7 day) and sysdate()
group by date(update_timestamp)
I had few problems with my query earlier which was solved on this post http://bit.ly/12irdyf .The problem is solved, however i need modification in my query, now i need to show null values as well.
Thanks

best way to keep a calender_table which has got entries for each date for the year.
select date(update_timestamp) as date, count(*) users
from calender_table c
left join registered r
on date(update_timestamp)=c.date
where c.date between date_sub(sysdate(), interval 7 day) and sysdate()
group by c.date

What do you mean by zero count values? Maybe you need:
select date(update_timestamp) as date, count(*) users, '0' AS zero
Maybe you wanted to fetch what date it was when you had no users?
Simply locate the date before the first user registered and assign a zero to it like above.

Related

Using SQL to get recent n days activity of every user in the table [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I have a table of game activity of users which looks something like this
So,for simplicity just consider account_id and date column. By now you might've understood that each record represents a player playing some game on a specific day. What I want to extract is the recent 15 days activity of every user counting backwards from his last game played. For eg,we have data that ranges from 4th April 2020 to 24 Sep 2020, Let's just say that a user played his last game on 20th Sep 2020 and hasn't played any game since then, so for that user I want his playing activity for the date ranging from 5th to 20th Sep(15 day back from his last game played) and I want to extract the same data for every user.
I've initially thought to implement this like ..... sort the table in descending order based on date and match the date with that specific account when that account_id appears for the first time(to create a dictionary in which key is account_id and value is last date he played) so that I can subtract 15 days from the value and filter the data for every account_id, but my co-worker isn't satisfied with that and is expecting to do all this within a single shot(using SQL query). Can someone guide me on how to do that. Thanks in advance :)
If I understood this correctly, you are basically looking for the MAX(Date) Grouped BY User as your starting (actually end) point.
It's easiest to put this in a subquery or CTE.
Then you can simply query your table again using the last date by user as your end date and calculate that date - 15 days as your start point.
This will retrieve all entries for users in the given period.
Example:
WITH BASE AS(
SELECT
MAX(Date) AS LastDate,
UserID
FROM GameActivity
GROUP BY UserID
)
SELECT
ga.UserID,
ga.Date
FROM GameActivity GA
JOIN BASE B ON b.UserID = ga.UserID
WHERE ga.Date >= DATE_SUB(b.LastDate, INTERVAL 15 DAY)
AND ga.Date <= b.LastDate
EDIT:
For getting the last 15 days regardless of actual dates, I would personally use a Window Function to count back
I split this into 2 CTEs to highlight the logic
WITH DistinctDates AS (
SELECT DISTINCT
user_id,
active_date
FROM userdata
),
DAYCount AS (
SELECT
user_id,
active_date,
COUNT(active_date) OVER (PARTITION BY user_id ORDER BY active_date DESC) AS ActiveDays
FROM DistinctDates
)
SELECT
dc.user_id,
ud.active_date,
dc.ActiveDays
FROM DayCount DC
JOIN userdata UD ON ud.user_id = dc.user_id AND ud.active_date = dc.active_date
WHERE ActiveDays BETWEEN 1 AND 15
ORDER BY dc.user_id, dc.ActiveDays ;
I tried this on MS SQL Server but MySQL should work the same
If you are running MySQL 8.0, you can do this with window functions:
select *
from (
select t.*, max(date) over(partition by account_id) max_date
from mytable t
) t
where date >= max_date - interval 15 day
In earlier versions, an alternative is a correlated subquery:
select *
from mytable t
where date >= (select max(t1.date) from mytable t1 where t1.account_id = t.account_id) - interval 15 day
Or using a join:
select *
from mytable t
inner join (select account_id, max(date) max_date from mytable group by account_id) m
on t.date >= m.max_date - interval 15 day

Count the number of items per hour, then find the average of the result

I'm trying to build a query to find average number of music tracks played per broadcast hour for a given day.
I have a table that logs when a track was played, based on a datetime value (created field).
So I need to count how many entries, or tracks, where logged per hour.
Then with the hourly totals, find the average.
So far I have this, but wondered if it is correct?
SELECT AVG(a.total) FROM (
SELECT HOUR(created) AS hour, COUNT(id) AS total
FROM `music_log` r
WHERE DATE(created) = DATE( DATE_SUB(NOW() , INTERVAL 1 DAY) ) group by HOUR(r.created)
) a
I got to admit, I formulated that from another post on stackoverflow, and don't understand what the a and r mean/reference.
I would like to know if I have this right, so I can expand query to cover a quarter (3 months) results.
You can calculate the average without a subquery:
SELECT COUNT(*) / COUNT(DISTINCT DATE(created), HOUR(created) ) as average
FROM `music_log`
WHERE QUARTER(created) = 1 AND YEAR(created) = YEAR(NOW()) ;
This calculates the total count and the number of hours without the need for a subquery.
As Strawberry says, it looks like I'm on the right track. Thanks Strawberry.
To expand this, and just in case it helps anyone else, I've included the query to cover a quarter, in this case the first quarter of the current year....he says hoping there's nothing else wrong with my query :)
SELECT AVG(a.total) FROM (
SELECT DATE(created) as day, HOUR(created) AS hour, COUNT(id) AS total
FROM `music_log`
WHERE QUARTER(created) = 1
AND YEAR(created) = YEAR(NOW())
group by DATE(created), HOUR(created)
) a
In order to calculate it correctly, I needed to group the hourly results by Date and Hour.
In the previous query in the question, it was only grouping by Hour. Which is fine if the Average calculation is over just a single day, but when you expand that beyond a day, the results become incorrect. This is because it will add the total of both occurrence of 11pm, for example, then work out the average.
Hope that helps...or if I've made a mistake, I hope someone picks up on it ;)

Select between dates with count condition

I have table user with column login_time.
I want to select all the users that have logged in more than 10 times in a month.
I tried something like this:
SELECT login_time, count(id) as loginCount FROM user
WHERE login_time between DATE_SUB(login_time INTERVAL 1 month) AND login_time
GROUP BY id, MONTH(login_time) HAVING loginCount > 10;
Im not sure about my selection between dates.
How can I select with a month intervals avoiding double records.
For example if I have this values for login_time:
1. '2015-02-01 14:05:19'
2. '2015-01-21 14:05:19'
3. '2015-01-11 14:05:19'
Both 3 and 2 are within month range of 1.
So will I get double records for that values?
To find the users who have logged in more than ten times in the month ending right now, do this.
SELECT COUNT(*) times_logged_in,
userid
FROM user
WHERE login_time >= NOW() - INTERVAL 1 MONTH
GROUP BY user_id
HAVING COUNT(*)> 10
To find the users who have logged in more than ten times in any calendar month in your table, do this.
SELECT COUNT(*) times_logged_in,
DATE(DATE_FORMAT(login_time, '%Y-%m-01')) month_beginning,
userid
FROM user
GROUP BY user_id, DATE(DATE_FORMAT(login_time, '%Y-%m-01'))
HAVING COUNT(*)> 10
The trick here is the expression DATE(DATE_FORMAT(login_time, '%Y-%m-01')), which converts any timestamp to the first day of the month in which it occurs.
Your question mentioned this WHERE condition:
WHERE login_time between DATE_SUB(login_time INTERVAL 1 month) AND login_time
This doesn't do anything interesting because it always comes back true. Each given login_time always falls in the interval you specified.
Edit: You can GROUP BY MONTH(dt) if you want. But the way I have shown it automatically accounts for years as well as months, so in my opinion it's much better for accurate reporting.
Another edit: This formula yields the preceding Sunday for any given date or timestamp item.
FROM_DAYS(TO_DAYS(login_time) -MOD(TO_DAYS(login_time) -1, 7))
If Monday is the first day of the week in your jurisdiction, change the -1 to -2. Grouping by this function is superior to doing GROUP BY WEEK(login_time) because WEEK() does odd things at the beginnings and ends of calendar years.
This is all written up in more detail here: http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/

Mysql - Find increased value over a week

Score Table
user_idx (int)
date (datetime)
score (int)
I need to find out how much total score has increased over a week from today's date. I know that I need two of the same user tables grouped by user_idx that one contains total scores from the past to today and the other contains total scores from the past to a date of a week ago.
After that, by substracting one from the other will give me the answer... but I'm struggling to write effective sql query that does it.
I've tried
SELECT BLAH BLAH
FROM (SELECT user_idx, COUNT(*) as last_week_study_amount
FROM user_table
WHERE date <= date_sub(now(),INTERVAL 1 WEEK)
GROUP BY user_idx)
AS a WHERE .....
Could you help me :( ?
Let me clear you want to get total count in last week.
Try below query
SELECT *
FROM (SELECT user_idx, COUNT(*) as last_week_study_amount
FROM user_table
WHERE date <= date_sub(now(),INTERVAL 1 WEEK)
GROUP BY user_idx)
AS a WHERE .....
SELECT (SUM(score) - last_week_score) AS increased_score,
FROM user a
JOIN (SELECT b.user_idx, COUNT(*) as last_week_score
FROM userb
WHERE date<= date_sub(now(),INTERVAL 1 WEEK)
GROUP BY b.user_idx) As c ON a.user_idx = c.user_idx
WHERE DATE(date) <= DATE(NOW())
GROUP BY a.user_idx
I ended up writing this code and I think this one is working okay... not sure if it's the best or has a critical error. I will update it if it turns out to be a bad one...

Count Distinct Active users per month

I am trying to calculate active monthly users based on signup and cancel dates. Several users have NULL cancel dates (since still active). This query has a bunch of users as just null action_year and action_month.
SELECT
T.action_year,
T.action_month,
COUNT(USerID) active_users
FROM
(
SELECT DISTINCT UserID, YEAR(SignupDate) action_year, MONTH(SignupDate) action_month FROM Stat
UNION
SELECT DISTINCT UserID, YEAR(CancelDate) action_year, MONTH(CancelDate) action_date FROM Stat
) T
GROUP BY
T.action_year,
T.action_month
ORDER BY
T.action_year ASC,
T.action_month ASC
Presumably active users are those where the month is somehow between the signup and cancel dates. This is tricky to define. Is it active on any date of the month? Active on the last day? Active on the first day?
I will assume the first. Active on any day during the month.
The idea is that the number of actives in a given month are all people who have signed up previously and not yet stopped. Given this observation, the calculation proceeds as follows:
Get a list of all years and months. The following query assumes that signups occur every month to simplify this part.
Use a correlated subquery to get the number of actives. This will do comparisons to the "yyyymm" form of the date.
Be sure to remember that CancelDate can be NULL.
The resulting query is:
select ym.the_year, ym.the_month,
(select count(*)
from stat s
where date_format(SignupDate, '%Y-%m') <= ym.yyyymm and
(CancelDate is null or date_format(CancelDate, '%Y-%m') >= ym.yyyymm)
) as NumActives
from (select distinct year(SignupDate) as the_year, month(SignupDate) as the_month,
date_format(SignupDate, '%Y-%m') as yyyymm
from stat
) ym