Group count results by date between two tables in mysql - mysql

I have 2 different tables, users and votes. I would like to get a query to count total users and total votes (not sum) by date.
users
id | created_at
votes
id | created_at
I want the result to look like this:
date | total users | total votes
2012-06-01 | 50 | 90
2012-06-01 | 23 | 0*
2012-06-01 | 80 | 12
*It should take into account that on some days no votes were made
I know how to make the 2 separate queries
SELECT DATE(created_at), count(id)
FROM vote
GROUP by DATE(created_at)
ORDER by DATE(created_at) DESC
but I don't know how to join them.

Try this:
SELECT
DATE(created_at),
SUM(CASE WHEN `Type` = 'Votes' THEN 1 ELSE 0 END) AS 'TotalVotes',
SUM(CASE WHEN `Type` = 'Users' THEN 1 ELSE 0 END) AS 'TotalUsers'
FROM
(
SELECT created_at, 'Votes' `Type` FROM vote
UNION ALL
SELECT created_at, 'Users' FROM Users
) t
GROUP by DATE(created_at)
ORDER by DATE(created_at) DESC

Try this:
SELECT created_at, SUM(votes) AS 'TotalVotes', SUM(users) AS 'TotalUsers'
FROM(SELECT DATE(created_at) created_at, COUNT(ID) votes, 0 users
FROM votes GROUP BY DATE(created_at)
UNION
SELECT DATE(created_at) created_at, 0 votes, COUNT(ID) users
FROM Users GROUP BY DATE(created_at)) t
GROUP BY created_at
ORDER BY created_at DESC

SELECT DATE(created_at),SUM(usercolumnname) as totalusers,
SUM(//votescolumnname) as totalvotes from users u,votes v
where u.date=v.date
GROUP BY u.DATE(created_at)
ORDER BY u.DATE(created_at) DESC

Related

MySQL - Get the max count from a subquery group

I have a table logins with the following schema:
| id | user_id | weekday |
|----|---------|---------|
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 2 |
...
Weekday is a number from 0 to 6.
I want to get which weekday has the highest count, for each user_id in the table.
I tried the following query:
SELECT MAX(num) as max_num, user_id, weekday
FROM (
SELECT COUNT(*) as num, user_id, weekday
FROM logins
GROUP BY user_id, weekday
) C
WHERE user_id = C.user_id AND num = C.num
GROUP BY user_id;
Which gets me weekday = 1 instead of 2. I think that I shouldn't use a WHERE clause here, but I couldn't manage to get the correct result.
I've checked other similar questions with no luck, such as:
MYSQL, Max,Group by and Max
Select first row in each GROUP BY group?
I created a SQL Fiddle with my example: http://sqlfiddle.com/#!9/e43a71/1
Here is a method:
SELECT user_id, MAX(num) as max_num,
SUBSTRING_INDEX(GROUP_CONCAT(weekday ORDER BY num DESC), ',', 1) as weekday_max
FROM (SELECT user_id, weekday, COUNT(*) as num
FROM logins l
GROUP BY user_id, weekday
) uw
GROUP BY user_id;
SELECT days.user_id, days.weekday, days.num
FROM (
SELECT user_id, MAX(num) AS num
FROM (
SELECT user_id, weekday, COUNT(*) AS num
FROM logins
GROUP BY user_id, weekday
) max
GROUP BY user_id
) nums
JOIN (
SELECT user_id, weekday, COUNT(*) as num
FROM logins
GROUP BY user_id, weekday
) days ON(days.user_id = nums.user_id AND days.num = nums.num);
-- With Mariadb 10.2 or MySQL 8.0.2
WITH days AS (
SELECT user_id, weekday, COUNT(*) as num
FROM logins
GROUP BY user_id, weekday
)
SELECT days.user_id, days.weekday, days.num
FROM (
SELECT user_id, MAX(num) AS num
FROM days
GROUP BY user_id
) nums
JOIN days ON(days.user_id = nums.user_id AND days.num = nums.num);

Cumulative Sum in MySQL

Using MySQL. I want to get cumulative sum.
This is my table
CREATE TABLE `user_infos`
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
(..)
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`) )
And what I want to get is
+-------+-------+----------------+
| month | count | cumulative_sum |
+-------+-------+----------------+
| 01 | 100 | 100 |
| 02 | 101 | 201 |
| ... | 110 | 311 |
| 12 | 200 | 511 |
+-------+-------+----------------+
but the result is
+-------+-------+----------------+
| month | count | cumulative_sum |
+-------+-------+----------------+
| 01 | 100 | 100 |
| 02 | 101 | 101 |
| ... | 110 | 110 |
| 12 | 200 | 200 |
+-------+-------+----------------+
This is my wrong query..
select
T1.Month,T1.Count,
#runnung_total := (#running_total + T1.Count) as cumulative_sum
from (
select date_format(created_at,'%m') as Month,count(1) as Count from users
where date_format(created_at,'%Y')='2016'
group by(date_format(created_at,'%m'))
union
select date_format(created_at,'%m') as Month,count(1) as Count from users
where date_format(created_at,'%Y')='2017'
group by(date_format(created_at,'%m')) ) as T1
join (select #running_total := 0) as R1;
I referred to this. What's wrong in my code?
You can achieve that in two steps: first of all get the sum for each year and month
select concat(year(created_at), lpad(month(created_at), 2, '0')) as ye_mo,
count(*) as cnt
from users
group by concat(year(created_at), lpad(month(created_at), 2, '0'))
Then join it with itself, having each row matched with all previous ones
select t1.ye_mo, sum(t2.cnt)
from (
select concat(year(created_at), lpad(month(created_at), 2, '0')) as ye_mo,
count(*) as cnt
from users
group by concat(year(created_at), lpad(month(created_at), 2, '0'))
) t1
join (
select concat(year(created_at), lpad(month(created_at), 2, '0')) as ye_mo,
count(*) as cnt
from users
group by concat(year(created_at), lpad(month(created_at), 2, '0'))
) t2
on t1.ye_mo >= t2.ye_mo
group by t1.ye_mo
order by t1.ye_mo
Edit
The query above assumes you want the running sum to increase across different years. If you want to display the months only, and aggregate the values of different years in the same month, you can change id this way
select t1.mnt, sum(t2.cnt)
from (
select month(created_at) as mnt,
count(*) as cnt
from userss
group by month(created_at)
) t1
join (
select month(created_at) as mnt,
count(*) as cnt
from userss
group by month(created_at)
) t2
on t1.mnt >= t2.mnt
group by t1.mnt
order by t1.mnt
Finally, if you want the running sum to reset at the beginning of each year, you can do that like this
select t1.yr, t1.mn, sum(t2.cnt)
from (
select year(created_at) as yr, month(created_at) as mn,
count(*) as cnt
from userss
group by year(created_at), month(created_at)
) t1
join (
select year(created_at) as yr, month(created_at) as mn,
count(*) as cnt
from userss
group by year(created_at), month(created_at)
) t2
on t1.yr = t2.yr and
t1.mn >= t2.mn
group by t1.yr, t1.mn
order by t1.yr, t1.mn
All three versions can be seen in action here
Variables are the right way to go. You can simplify your query:
select m.Month, m.cnt,
(#running_total := (#running_total + m.cnt) ) as cumulative_sum
from (select month(created_at) as Month, count(*) as cnt
from users
where year(created_at) in (2016, 2017)
group by month(created_at)
) m cross join
(select #running_total := 0) params
order by m.Month;
Starting with MySQL 8, the ideal approach to calculate cumulative sums is by using SQL standard window functions rather than the vendor-specific, and not stricly declarative approach of using local variables. Your query can be written as follows:
WITH data(month, count) AS (
SELECT date_format(create_at, '%m') AS month, count(*) AS count
FROM users
GROUP BY date_format(create_at, '%m')
)
SELECT
month,
count,
sum(count) OVER (ORDER BY month) AS cumulative_sum
FROM data

Count records which fulfil given condition

I have a table with the following schema:
+-------------------------------------------------------+
| table_counter |
+----+---------------------+------------+---------------+
| id | timestamp | entry_type | country |
+----+---------------------+------------+---------------+
+----+---------------------+------------+---------------+
| 10 | 2017-05-01 12:00:00 | click | Germany |
+----+---------------------+------------+---------------+
| 11 | 2017-05-01 12:00:00 | view | Austria |
+----+---------------------+------------+---------------+
| 12 | 2017-05-01 12:00:00 | click | UK |
+----+---------------------+------------+---------------+
| 13 | 2017-05-01 12:00:00 | view | USA |
+----+---------------------+------------+---------------+
I need to return the following result: Select the sum of views and clicks of the top 5 countries by sum of views in the past 30 days.
I know how to count the records all right, but how do I define the constrains? How do I return all entries from five countries with the highest number of views?
Limiting the result to the last 30 days is trivial, but I'm pretty much stuck at the beginning.
Using order by and limit keywords,
SELECT SUM(IF(entry_type = "view", 1, 0)) as view_count FROM t3 GROUP BY country, entry_type ORDER BY view_count DESC LIMIT 5
--EDIT
As per the requirement stated in the comments, here's the updated query:
SELECT SUM(view_click_count) as all_total FROM (SELECT country, SUM(IF(entry_type = "view", 1, 0)) as view_count, SUM(IF(entry_type = "click", 1, 0)) as click_count, count(entry_type) as view_click_count FROM t3 GROUP BY country ORDER BY view_count DESC LIMIT 5) t2
all_total gives the total count as needed, for top 5 countries.
You can do it this way:
select
tc.country,
count(case entry_type when 'click' then 1 else null end) clicks,
count(case entry_type when 'view' then 1 else null end) views
from table_counter tc
inner join (
select top 5 country from [dbo].[table_counter]
where entry_type = 'view'
and timestamp >= DATEADD(DAY, -30, GETDATE())
group by country
order by count(entry_type) desc
) t on t.country = tc.country
where timestamp >= DATEADD(DAY, -30, GETDATE())
group by tc.country
order by views desc
This is for SQL Server. A few tweaks might be needed for MySQL (i.e. 'Limit' instead of 'TOP')
You can get top 5 countries by views with the following query, e.g.:
SELECT country, count(*) as 'views'
FROM table
WHERE timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()
AND entry_type = 'view'
GROUP BY country
ORDER BY count(*) DESC
LIMIT 5
Now, to select clicks, you can add another query in SELECT , e.g.:
SELECT t.country, COUNT(*) as 'views',
(SELECT COUNT(*)
FROM `table`
WHERE country = t.country
AND entry_type = 'click'
AND timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()
) as 'clicks'
FROM `table` t
WHERE t.timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()
AND t.entry_type = 'view'
GROUP BY t.country
ORDER BY count(*) DESC
LIMIT 5
Here's the SQL Fiddle.
Update
To get the SUM of views and clicks, wrap the above query into another SELECT, e.g.:
SELECT country, views + clicks
FROM(
SELECT t.country, COUNT(*) as 'views',
(SELECT COUNT(*)
FROM `table`
WHERE country = t.country
AND entry_type = 'click'
AND timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()
) as 'clicks'
FROM `table` t
WHERE t.timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()
AND t.entry_type = 'view'
GROUP BY t.country
ORDER BY count(*) DESC
LIMIT 5
) b;
Here's the updated SQL Fiddle.

Calculate index position using sub queries on group by using date

select user_id as sponsor_id,sum(points),created_at
from points_history
where created_at between '2014/08/12' and '2015/08/12' and transaction_type="debit"
group by user_id,DATE_FORMAT(created_at,"%d %M %Y")
order by DATE_FORMAT(created_at,"%d %M %Y"),sum(points) desc
sponsor_id sum(points) created_at
1 30 2014-12-08 10:54:59
2 25 2014-12-09 05:43:11
3 20 2014-12-09 06:58:40
1 5 2014-12-09 05:56:12
1 34 2014-08-23 10:42:32
here I want to calculate rank of particular sponsor using sponsor_id on daily basis .. I want to build a query that can return me something like as displayed below:
sponsor_id rank created_at
1 1 2014-12-08 10:54:59
1 3 2014-12-09 05:56:12
1 1 2014-08-23 10:42:32
I think I can use sub query like
select *
from (select user_id as sponsor_id,sum(points),created_at
from points_history
where created_at between '2014/08/12' and '2015/08/12' and transaction_type="debit"
group by user_id,DATE_FORMAT(created_at,"%d %M %Y")
order by DATE_FORMAT(created_at,"%d %M %Y"),sum(points) desc
) as t
where t.sponsor_id = 1
but how to calulate rank here.
Try this:
SELECT sponsor_id, points, created_at,
IF(#dte=#dte:=DATE(created_at), #rank:=#rank+1, #rank:=1) AS rank
FROM (SELECT user_id AS sponsor_id, SUM(points) points, created_at
FROM points_history
WHERE created_at BETWEEN '2014-08-12' AND '2015-08-12' AND
transaction_type = "debit"
GROUP BY user_id, DATE_FORMAT(created_at,"%d %M %Y")
ORDER BY DATE(created_at), SUM(points) DESC
) AS A, (SELECT #rank:=0, #dte:='') AS B
ORDER BY DATE(created_at), points DESC;

Query for Count on coulmns by condition with Group BY

Table structure
id(int)
dated_on(datetime)
(many_more)
Table having thousands of data grouped by id.
I want result as id, count of that id,
also count of id having date difference is week.(i.e. count of data last week)
select
id,
count(id) as cnt,
sum(if(now() - interval 7 day <= dated_on,1,0)) as lastweek
from table
group by id
SELECT
id,
COUNT(*),
SUM(CASE WHEN dated_on = CURDATE() - 7 THEN 1 ELSE 0 END)
FROM
yourTable
GROUP BY
id
I also got solution:
SELECT id, count(id) as counter,
COUNT(CASE WHEN DATEDIFF( CURDATE(), dated_on ) < 7 THEN 1 ELSE NULL END) AS latest_count
FROM my_table
GROUP BY id