I've an activity tracker table with activity_id (Primary key, auto increment), user_id, api_function and date_added fields (please find the screenshot attached).
By using the below query I was able to count the number of entries per user in the last 28 days:
SELECT COUNT( DISTINCT date(date_Added) ) AS day_of_activity, user_id
FROM activity_tracker
WHERE date_added >= DATE( NOW() ) - INTERVAL 28 DAY
GROUP BY user_id
LIMIT 0 , 30
like:
days_of_activity user_id
34 1
1 3
13 9
2 10
1 11
8 12
I need to track the count of users who have:
more than 16 entries in the past 28 days
between 6 to 16 in the past 28 days,
1 to 6 in the past 28 days,
no entries in the past 30 days,
no entries in the past 90 days and
no entries in the past 180 days.
Is it possible to do this in single mysql query?
Please help me.
Thanks in advance.
Please find the answer below:
SELECT
SUM(
CASE WHEN day_of_activity>16 AND last_activity_date >= DATE(NOW()) - INTERVAL 28 DAY
THEN 1 ELSE 0
END)
as daily_users_count,
SUM(
CASE WHEN day_of_activity>6 AND day_of_activity <=16 AND last_activity_date >= DATE(NOW()) - INTERVAL 28 DAY
THEN 1 ELSE 0
END)
as weekly_users_count,
SUM(
CASE WHEN day_of_activity>=1 AND day_of_activity <=6 AND last_activity_date >= DATE(NOW()) - INTERVAL 28 DAY
THEN 1 ELSE 0
END)
as monthly_users_count,
SUM(
CASE WHEN DATE_SUB(CURDATE(),INTERVAL 30 DAY) >= last_activity_date OR last_activity_date IS NULL
THEN 1 ELSE 0
END)
as not_in_30,
SUM(
CASE WHEN DATE_SUB(CURDATE(),INTERVAL 90 DAY) >= last_activity_date OR last_activity_date IS NULL
THEN 1 ELSE 0
END)
as not_in_90,
SUM(
CASE WHEN DATE_SUB(CURDATE(),INTERVAL 180 DAY) >= last_activity_date OR last_activity_date IS NULL
THEN 1 ELSE 0
END)
as not_in_180
FROM (
SELECT COUNT(DISTINCT date(at.date_added)) as day_of_activity, a.user_id, max(at.date_added) as last_activity_date
FROM accounts a
LEFT JOIN activity_tracker at ON a.user_id = at.user_id
WHERE a.user_role_id = 2
GROUP BY a.user_id
)temp
try this query
select CASE WHEN dates_of_activity>16 THEN count(*) ELSE
CASE WHEN dates_of_activity>6 and dates_of_activity<17 THEN count(*) ELSE CASE WHEN dates_of_activity>0 and dates_of_activity<7 THEN count(*) ELSE count(*)
END
END END as count,
CASE WHEN dates_of_activity>16 THEN 'Above 16' ELSE
CASE WHEN dates_of_activity>6 and dates_of_activity<17 THEN ' >6 and<16' ELSE CASE WHEN dates_of_activity>0 and dates_of_activity<7 THEN '>0 and <6' ELSE 'never use'
END
END END as typeday
from (SELECT COUNT( DISTINCT date( date_Added ) ) AS dates_of_activity
, user_id
FROM activity_tracker
WHERE date_added >= DATE( NOW( ) ) - INTERVAL 28 DAY
GROUP BY user_id)a
LIMIT 0 , 30
Related
In my Mysql DB I have a column called 'mydate' with stored unix timestamps.
I need to get the SUM of all records, that:
has the tomorrow's date (the time shouldn't be taken into consideration, only date)
has today +2 days
has today +3 days
etc., but max. today +7 days
I did it this way (just an example for 2 upcoming days):
$sqld = "SELECT
sum(case when mydate= (CURDATE() + INTERVAL 1 DAY) then 1 else 0 end) AS p1,
sum(case when mydate= (CURDATE() + INTERVAL 2 DAY) then 1 else 0 end) AS p2
FROM mytable";
$rsld = $conn->Execute($sqld);
$x1=$rsld->fields['p1'];$x2=$rsld->fields['p2'];
But when I echo the $x1 and $x2, there is only 0 for both. Definitely there are records for tomorrow and after tomorrow in my table.
Am I doing it wrong?
If you are storing the datetimes as unix timestamps then you need to convert to datetime first and then remove the time portion. Try this
select
sum(case when date(FROM_UNIXTIME(mydate))=DATE_ADD(CURDATE(),INTERVAL 1 DAY) then 1 else 0 end) p1,
sum(case when date(FROM_UNIXTIME(mydate))=DATE_ADD(CURDATE(),INTERVAL 2 DAY) then 1 else 0 end) p2,
sum(case when date(FROM_UNIXTIME(mydate))=DATE_ADD(CURDATE(),INTERVAL 3 DAY) then 1 else 0 end) p3,
sum(case when date(FROM_UNIXTIME(mydate))=DATE_ADD(CURDATE(),INTERVAL 4 DAY) then 1 else 0 end) p4,
sum(case when date(FROM_UNIXTIME(mydate))=DATE_ADD(CURDATE(),INTERVAL 5 DAY) then 1 else 0 end) p5,
sum(case when date(FROM_UNIXTIME(mydate))=DATE_ADD(CURDATE(),INTERVAL 6 DAY) then 1 else 0 end) p6,
sum(case when date(FROM_UNIXTIME(mydate))=DATE_ADD(CURDATE(),INTERVAL 7 DAY) then 1 else 0 end) p7
from mytable
where date(FROM_UNIXTIME(mydate))>DATE_ADD(CURDATE(),INTERVAL 1 DAY) and date(FROM_UNIXTIME(mydate))<=DATE_ADD(CURDATE(),INTERVAL 7 DAY);
Ok, I think I found an easier solution.
I just define tomorrows and others date as variables:
$date = date("Y-m-d");
$d1 = strtotime('+1 day', strtotime($date));
$d2 = strtotime('+2 day', strtotime($date));
and then just simply compare in Mysql:
sum(case when mydate = $d1 then 1 else 0 end) AS p1
It looks like you're actually looking to count the number of rows instead of sum a particular field. You can use WHERE and GROUP BY to make it easier
SELECT mydate, count(id) as total
FROM mytable
WHERE STR_TO_DATE(mydate, '%Y-%m-%d') BETWEEN DATE_ADD(CURDATE(),INTERVAL 1 DAY) AND DATE_ADD(CURDATE(),INTERVAL 7 DAY)
This does turn it into multiple rows instead of a single row as your original query.
I need to get Posts count of closed and open posts grouped by date and with 1 week interval. what i'm getting right now is.
what i want is, like this.
sorry for poor explanation.
Thanks!
Please give it a try:
SELECT
date_posts - INTERVAL WEEKDAY(date_posts) DAY AS date_start,
date_posts + INTERVAL (6 - WEEKDAY(date_posts)) DAY AS date_start,
COUNT(*) totalPosts,
COUNT(CASE WHEN Close_count = 1 THEN 1 END) closeCount,
COUNT(CASE WHEN Open_count = 1 THEN 1 END) openCount
FROM your_table
GROUP BY YEARWEEK(date_posts,1);
Note:
YEARWEEK(date) function assumes the start date is Sunday.
YEARWEEK(date,1) function assumes the start date is Monday.
WEEKDAY(date) function returns the index of the date of the corresponding week assuming Monday as the start day of a week. It returns 0 for Monday, 1 for Tuesday ..... and 6 for Sunday.
EDIT:
For date range search:
SELECT
date_posts - INTERVAL WEEKDAY(date_posts) DAY AS date_start,
date_posts + INTERVAL (6 - WEEKDAY(date_posts)) DAY AS date_start,
COUNT(*) totalPosts,
COUNT(CASE WHEN Close_count = 1 THEN 1 END) closeCount,
COUNT(CASE WHEN Open_count = 1 THEN 1 END) openCount
FROM your_table
WHERE date_posts >= YOUR_START_DATE - INTERVAL WEEKDAY(YOUR_START_DATE) DAY
AND date_posts <= YOUR_END_DATE + INTERVAL (6 - WEEKDAY(YOUR_END_DATE)) DAY
GROUP BY YEARWEEK(date_posts,1);
So, based on this if you want to grab the result of last 5 weeks including current week you need to run the following query:
SELECT
date_posts - INTERVAL WEEKDAY(date_posts) DAY AS date_start,
date_posts + INTERVAL (6 - WEEKDAY(date_posts)) DAY AS date_start,
COUNT(*) totalPosts,
COUNT(CASE WHEN Close_count = 1 THEN 1 END) closeCount,
COUNT(CASE WHEN Open_count = 1 THEN 1 END) openCount
FROM your_table
WHERE date_posts >= (CURDATE() - INTERVAL 5 WEEK) - INTERVAL WEEKDAY(CURDATE() - INTERVAL 5 WEEK) DAY
AND date_posts <= CURDATE() + INTERVAL (6 - WEEKDAY(CURDATE())) DAY
GROUP BY YEARWEEK(date_posts,1);
SELECT DATE(i.time_added - INTERVAL WEEKDAY(i.time_added)DAY) AS date_start,
DATE(i.time_added + INTERVAL (6 - WEEKDAY(i.time_added))DAY ) AS date_end,
COUNT(DISTINCT i.lead_id) AS Leads_count,
COUNT(DISTINCT CASE WHEN i.`stage` IN
('6','8') THEN i.`lead_id`
ELSE NULL END) AS Close_count
,COUNT(DISTINCT CASE WHEN i.`stage` IN ('1','2','3','4','5','7','9')
THEN i.`lead_id` ELSE NULL END) AS Open_count
FROM tbl i
INNER JOIN tbl s ON i.stage = s.status_id
INNER JOIN tbl u ON i.assigned_to = u.userid
INNER JOIN tbl p ON p.purpose_id = i.purpose
WHERE 1=1 AND i.STATUS = 'on' AND i.time_added >= DATE_SUB(NOW(),INTERVAL 5 WEEK) -
INTERVAL WEEKDAY(DATE_SUB(NOW(),INTERVAL 5 WEEK)) DAY
AND i.time_added <= NOW() + INTERVAL (6 - WEEKDAY(NOW())) DAY
GROUP BY YEARWEEK(i.time_added,1)
I need to perform two different counts in one single query.
First Query: count number of transactions from today 30 days back.
Second Query: count number of transactions from last 60 until last 30 days.
I have first query working fine as:
SELECT
COUNT(*) AS sales
FROM
transactions
WHERE DATE(created) > DATE_SUB(NOW(), INTERVAL 30 DAY)
AND STATUS = 1;
How can I incorporate the second query into the above?
You can use COUNT and CASE WHEN:
SELECT
COUNT(CASE WHEN DATE(created) > DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 END) AS c1,
COUNT(CASE WHEN DATE(created) <= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 END) AS c2
FROM transactions
WHERE DATE(created) > DATE_SUB(NOW(), INTERVAL 60 DAY)
AND STATUS = 1;
or UNION:
SELECT COUNT(*) AS sales
FROM transactions
WHERE DATE(created) > DATE_SUB(NOW(), INTERVAL 30 DAY)
AND STATUS = 1
UNION ALL
SELECT COUNT(*)
FROM transactions
WHERE DATE(created) > DATE_SUB(NOW(), INTERVAL 60 DAY)
AND DATE(created) < DATE_SUB(NOW(), INTERVAL 30 DAY)
AND STATUS = 1
I made the following query to select persons by age group, as count and as percentage. Ages are stored as 0000-00-00 in my database.
SELECT AgeGroup, count(*) AS count, ROUND(sum( 100 ) / total) AS percentage
FROM (
SELECT case
when age between 0 and 17 then '00 - 17'
when age between 18 and 24 then '18 − 24'
when age between 25 and 34 then '25 − 34'
when age between 35 and 44 then '35 − 44'
when age between 45 and 54 then '45 − 54'
when age between 55 and 64 then '55 − 64'
when age between 65 and 125 then '65+'
else 'Unknown'
end AS AgeGroup
FROM (
SELECT ROUND(DATEDIFF(Cast(NOW() as Date),
Cast(dateofbirth as Date)) / 365, 0) as age
FROM people
) as SubQueryAlias
) as SubQueryAlias2
CROSS JOIN (SELECT count( * ) AS total FROM people)x
group by
AgeGroup
The current result is:
AgeGroup | count | percentage
00 - 17 33 1
18 − 24 235 5
.. .. ..
What I need is a addition to the query to separate the results in male/female/unknown:
AgeGroup | gender | count | percentage
00 - 17 M 33 1
00 - 17 F 33 1
.. .. .. ..
You might have the easiest time by defining range-tables. This also prevents you from needing to do date math on every entry, and so may be more efficient for the grouping.
First, a range table for ages:
SELECT '00 - 17' AS ageGroup, CURRENT_DATE AS lower, CURRENT_DATE - INTERVAL 18 YEAR AS upper
UNION ALL
SELECT '18 - 24', CURRENT_DATE - INTERVAL 18 YEAR, CURRENT_DATE - INTERVAL 25 YEAR
UNION ALL
SELECT '25 - 34', CURRENT_DATE - INTERVAL 25 YEAR, CURRENT_DATE - INTERVAL 35 YEAR
UNION ALL
SELECT '35 - 44', CURRENT_DATE - INTERVAL 35 YEAR, CURRENT_DATE - INTERVAL 45 YEAR
UNION ALL
SELECT '45 - 54', CURRENT_DATE - INTERVAL 45 YEAR, CURRENT_DATE - INTERVAL 55 YEAR
UNION ALL
SELECT '55 - 64', CURRENT_DATE - INTERVAL 55 YEAR, CURRENT_DATE - INTERVAL 65 YEAR
UNION ALL
SELECT '65+', CURRENT_DATE - INTERVAL 65 YEAR, null
UNION ALL
SELECT 'Unknown', null, null
SQL FIddle Demo
...which generates a table about like you'd expect. Note that the upper-bound is exclusive, which is why it uses the same value as the lower bound of the next row. Note also that 1) the '65+' bracket has no upper bound, and 2) the 'Unknown' bracket has neither.
Of course, we also need a Gender table:
SELECT 'M' AS gender
UNION ALL
SELECT 'F'
UNION ALL
SELECT 'Unknown'
(As a side note, I'd normally be using a multi-line VALUES(...) statements, but SQL Fiddle seems to dislike the syntax in subqueries for MySQL for some reason. Use whichever you're comfortable with.)
There's one last piece of knowledge we need:
Specifically, COUNT(<expression>) will ignore null rows. We can thus stitch together the full query similarly to:
SELECT AgeRange.ageGroup, Gender.gender,
COUNT(People.id), ROUND(100 * COUNT(People.id) / Total.countOfPeople) AS percentage
FROM (SELECT '00 - 17' AS ageGroup, CURRENT_DATE AS lower, CURRENT_DATE - INTERVAL 18 YEAR AS upper
UNION ALL
SELECT '18 - 24', CURRENT_DATE - INTERVAL 18 YEAR, CURRENT_DATE - INTERVAL 25 YEAR
UNION ALL
SELECT '25 - 34', CURRENT_DATE - INTERVAL 25 YEAR, CURRENT_DATE - INTERVAL 35 YEAR
UNION ALL
SELECT '35 - 44', CURRENT_DATE - INTERVAL 35 YEAR, CURRENT_DATE - INTERVAL 45 YEAR
UNION ALL
SELECT '45 - 54', CURRENT_DATE - INTERVAL 45 YEAR, CURRENT_DATE - INTERVAL 55 YEAR
UNION ALL
SELECT '55 - 64', CURRENT_DATE - INTERVAL 55 YEAR, CURRENT_DATE - INTERVAL 65 YEAR
UNION ALL
SELECT '65+', CURRENT_DATE - INTERVAL 65 YEAR, null
UNION ALL
SELECT 'Unknown', null, null) AgeRange
CROSS JOIN (SELECT 'M' AS Gender
UNION ALL
SELECT 'F'
UNION ALL
SELECT 'Unknown') Gender
CROSS JOIN (SELECT COUNT(*) countOfPeople
FROM People) Total
LEFT JOIN People
ON ((People.dateOfBirth > AgeRange.upper AND dateOfBirth <= AgeRange.lower)
OR (People.dateOfBirth <= AgeRange.lower AND AgeRange.upper IS NULL)
OR (AgeRange.lower IS NULL AND AgeRange.upper IS NULL AND People.dateOfBirth IS NULL))
AND (Gender.gender = People.gender
OR Gender.gender = 'Unknown' AND People.gender IS NULL)
GROUP BY AgeRange.ageGroup, Gender.gender
SQL Fiddle Demo
(note the Fiddle demo uses the date of this post, '2014-07-21', as CURRENT_DATE, to make the age range query stable for future readers).
I really hope I am wrong about it ....but would the reason of constant error be...you didn't select the gender?
Also, a nerdy side note, 365 days doesn't make a year, it's roughly 365.25 days XD which mean your equation is slightly off haha
SELECT AgeGroup, gender, count(*) AS count, ROUND(sum( 100 ) / total) AS percentage
FROM (
SELECT case
when age between 0 and 17 then '00 - 17'
when age between 18 and 24 then '18 − 24'
when age between 25 and 34 then '25 − 34'
when age between 35 and 44 then '35 − 44'
when age between 45 and 54 then '45 − 54'
when age between 55 and 64 then '55 − 64'
when age between 65 and 125 then '65+'
else 'Unknown'
end AS AgeGroup, gender
FROM (
SELECT ROUND(DATEDIFF(Cast(NOW() as Date),
Cast(dateofbirth as Date)) / 365, 0) as age,
gender
FROM people
) as SubQueryAlias
) as SubQueryAlias2
CROSS JOIN (SELECT count( * ) AS total FROM people)x
group by
AgeGroup, gender
I want to display users stats on my website, returning the percentage of age groups like :
-13 years : $percent %
13-15 years : $percent %
15-20 years : $percent %
23+ : $percent %
In my mysql table i have a column birth_date returning datatime (yyyy-mm-dd).
Did you have hints or idea to do that ?
Pure SQL:
SELECT
`group`,
COUNT(*) as `count`
FROM
`user`
INNER JOIN (
SELECT
0 as `start`, 12 as `end`, '0-12' as `group`
UNION ALL
SELECT
13, 14, '13-14'
UNION ALL
SELECT
15, 19, '15-19'
UNION ALL
SELECT
20, 150, '20+'
) `sub`
ON TIMESTAMPDIFF(YEAR, `birth_date`, NOW()) BETWEEN `start` AND `end`
GROUP BY `group` WITH ROLLUP;
Anything else might be calculated via PHP.
select case when year(curdate() - birth_date) < 13
then '< 13 years'
when year(curdate() - birth_date) between 13 and 14
then '13 - 14 years'
when year(curdate() - birth_date) between 15 and 20
then '15 - 20 years'
when year(curdate() - birth_date) >= 23
then '+23 years'
end as `description`,
(select count(*) from your_table) / count(*)
from your_table
group by case when year(curdate() - birth_date) < 13 then 1
when year(curdate() - birth_date) between 13 and 14 then 2
when year(curdate() - birth_date) between 15 and 20 then 3
when year(curdate() - birth_date) >= 23 then 4
end
If you want pure sql:
SELECT COUNT(*)
FROM [table_name]
WHERE birth_date < DATE_ADD(NOW(),INTERVAL 13 YEAR)
WHERE birth_date >= DATE_ADD(NOW(),INTERVAL 13 YEAR)
AND birth_date < DATE_ADD(NOW(),INTERVAL 15 YEAR)
--etc
else I would suggest using php to create the dates.
You could union all the queries together and create and sql view eg:
CREATE VIEW statistics AS
SELECT "0-13" as age ,COUNT(*) as total
FROM table_name
WHERE birth_date < DATE_ADD(NOW(),INTERVAL 13 YEAR)
UNION
SELECT "13-15" as age ,COUNT(*) as total
FROM table_name
WHERE birth_date >= DATE_ADD(NOW(),INTERVAL 13 YEAR)
AND birth_date < DATE_ADD(NOW(),INTERVAL 15 YEAR)
UNION
SELECT "15-20" as age ,COUNT(*) as total
FROM table_name
WHERE birth_date >= DATE_ADD(NOW(),INTERVAL 15 YEAR)
AND birth_date < DATE_ADD(NOW(),INTERVAL 20 YEAR)
UNION
SELECT "20-23" as age ,COUNT(*) as total
FROM table_name
WHERE birth_date >= DATE_ADD(NOW(),INTERVAL 20 YEAR)
AND birth_date < DATE_ADD(NOW(),INTERVAL 23 YEAR)
UNION
SELECT "23+" as age ,COUNT(*) as total
FROM table_name
WHERE birth_date >= DATE_ADD(NOW(),INTERVAL 23 YEAR)
Then you can just query:
SELECT * from statistics
Return datetime as a timestamp, compare with now.offset(years=??), categorize in arrays then count records.