I have the following 2 tables
id km date
1 22.1 2012-04-15
2 52.1 2012-04-14
3 72.1 2012-03-15
4 54.1 2012-03-14
and
id lt date
1 16.4 2012-04-03
2 22.6 2012-04-29
3 45.9 2012-03-2
4 13.1 2012-03-31
From this tables I need to get a rate by month, I mean, the number of km divided by number of lt in a month. I know I can get the sum of km by month using aggregate functions in a query and in other query the sum of lt, something like this.
SELECT SUM(km) FROM kilometers GROUP BY MONTH(date)
SELECT SUM(lt) FROM gasoline GROUP BY MONTH(date)
Then I could manually divide km/lt for each grouped month. So the question is, can I do that on a simple query? Do I have to change the structure of my database?
Any help would be appreciated, thanks.
You should be able to just join the table data together by the month/year. You would just need to handle the situation where lt is 0 or null for a month. Note that you will also want to group by YEAR so it handles multi-year data appropriately:
SELECT k.year, k.month, k.km / g.lt
FROM (SELECT YEAR(date) as year, MONTH(date) as month, SUM(km) as km
FROM kilometers GROUP BY YEAR(date), MONTH(date)) k
JOIN (SELECT YEAR(date) as year, MONTH(date) as month, SUM(lt) as lt
FROM gasoline GROUP BY YEAR(date), MONTH(date)) g
ON k.month = g.month AND k.year = g.year
Sample output:
YEAR MONTH K.KM / G.LT
---- ----- --------------
2012 3 2.138982929974
2012 4 1.90256407322
Demo: http://www.sqlfiddle.com/#!2/c2942/6
Related
I have an orders table
Order_id User_id Order_date
1 32 2020-07-19
2 24 2020-07-21
3 27 2020-07-27
4 24 2020-08-14
5 32 2020-08-18
6 32 2020-08-19
7 58 2020-08-20
Now I want to find how many of the users ordered in 1st month also ordered in the next month. In this case, user_id's 32,24,27 ordered in 7th month but only 24 and 32 ordered in the next month.
I want the result to be like :
Date Retained_Users Total_users
2020-07 Null 3
2020-08 2 3
I'm lost here. Can someone please help me with this?
In MySQL 8.0, you can do this with window functions:
select
order_month,
count(distinct case when cnt_orders_last_month > 0 then user_id end) retained_users,
count(distinct user_id) total_users
from (
select
user_id,
date_format(order_date, '%Y-%m-01') as order_month,
count(*) over(
partition by user_id
order by date(date_format(order_date, '%Y-%m-01'))
range between interval 1 month preceding and interval 1 day preceding
) cnt_orders_last_month
from mytable
) t
group by order_month
The logic lies in the range specification of the window function; it orders record by month, and counts how many orders the customer placed last month. Then all that is left to do is aggregate and count distinct users.
Demo on DB Fiddle
Here's my table, showing user names and the timestamp they scored a point:
id user date
1 Aaron 23/02/2012 22:44
2 Betty 23/02/2012 22:47
3 Carlos 24/02/2012 16:01
4 David 28/02/2012 11:40
5 David 28/02/2012 12:32
6 David 28/02/2012 16:59
7 Aaron 2/03/2012 13:46
8 Aaron 30/03/2012 18:37
9 Betty 30/03/2012 19:58
10 Emma 9/04/2012 6:49
11 Emma 9/04/2012 13:19
12 Emma 9/04/2012 18:20
13 Emma 9/04/2012 20:46
14 Aaron 10/04/2012 15:47
15 Betty 10/04/2012 19:15
16 Betty 10/04/2012 20:40
17 Carlos 11/04/2012 9:44
18 Carlos 11/04/2012 20:01
19 David 11/04/2012 23:17
20 David 12/04/2012 17:09
And here is the results table I am trying to achieve, i.e. an x axis showing month-year, and a y axis displaying the number of users who reached a certain points threshold within that month:
date 1 point First time? 2 points First time? 3 points First time? 4 points First time? Total
Feb-12 A,B,C A,B,C D D 4
Mar-12 B A A 3
Apr-12 A,B,C B,C,D B,C,D E E 4
I've only got as far as calculating the total number of points and the total number of distinct scorers within a given month:
SELECT DISTINCT CONCAT (MONTHNAME(date), ' ', YEAR(date)) as 'date', COUNT(id) as total_points, COUNT(distinct referrer_id) as number_of_scorers
from points
group by CONCAT (MONTH(date), ' ', YEAR(date))
order by YEAR(date), MONTH(date)
which is only giving me:
date total_points number_of_scorers
Feb-12 6 4
Mar-12 3 3
etc.
So my questions are:
How can I amend the query to show me which users reached each point threshold within each month?
How can I amend the query to show me which users reached each point threshold for the first time within that month?
Thanks
The basic query you need is this:
select date_format(date, '%Y-%m') as yyyymm, user, count(*) as points
from t
group by date_format(date, '%Y-%m') as yyyymm, user;
This gets the number of points for each user in a month.
The rest is just aggregations, joins, and conditions:
select ymu.yyyymm,
group_concat(case when ymu.points = 1 then user end) as Points1_Users,
group_concat(case when ymu.points = 1 and ymu.yyyymm = u.min_yyyymm then user end) as Points1_Users_First,
group_concat(case when ymu.points = 2 then user end) as Points2_Users,
group_concat(case when ymu.points = 2 and ymu.yyyymm = u.min_yyyymm then user end) as Points2_Users_First
from (select date_format(date, '%Y-%m') as yyyymm, user, count(*) as points
from t
group by date_format(date, '%Y-%m') as yyyymm, user
) ymu join
(select user, min(yyyymm) as min_yyyymm
from (select date_format(date, '%Y-%m') as yyyymm, user, count(*) as points
from t
group by date_format(date, '%Y-%m') as yyyymm, user
) t
group by user
) u
on ymu.user = u.user
group by yyyymm
order by yyyymm;
I have a big view called: how_many_per_month
name_of_product | how_many_bought | year | month
p1 20 2012 1
p2 7 2012 1
p1 10 2012 2
p2 5 2012 2
p1 3 2012 3
p2 20 2012 3
p3 66 2012 3
How to write MySQL query in order to get only first few occurences of product p1, p2, p3 at once?
To get it one by one for first 3 months I can write:
SELECT name_of_product , sum(how_many_bought) FROM
(SELECT name_of_product, how_many_bought FROM `how_many_per_month`
WHERE name_of_product= 'p1' LIMIT 3) t
How to do it to all possible products at once so my result for taking only first month is like:
p1 20
p2 7
p3 66
For two months:
p1 30
p2 12
p3 66
The problem is that some products are published in different months and I have to make statistic how many of total of them are sold in first month, first 3 months, 6 months, 1 year divided by total.
Example using union
select
name_of_product,
sum(how_many_bought) as bought,
"first month" as period
from how_many_per_month
where month = 1
group by name_of_product
union
select
name_of_product,
sum(how_many_bought) as bought,
"first 2 month" as period
from how_many_per_month
where month <= 2
group by name_of_product
union
select
name_of_product,
sum(how_many_bought) as bought,
"first 6 month" as period
from how_many_per_month
where month <= 6
group by name_of_product
union
select
name_of_product,
sum(how_many_bought) as bought,
"first 12 month" as period
from how_many_per_month
where month <= 12
group by name_of_product
Demo: http://www.sqlfiddle.com/#!2/788ea/11
Results are different a little bit from your expectation. Are you sure that you write them properly? If you need to gain more speed in query time you can use group by case as I've already said.
I'm not quite sure what you're trying to achieve as the description of your question is a bit unclear. From what I've read so far, I understand you want to show the total of how many ITEM_X, ITEM_Y, ITEM_Z were sold for the past 1,3,6 months.
Based on the data you've provided, I've created this sqlfiddle that sums all results and groups them by item. This is the query:
SELECT
name_of_product,
sum(how_many_bought) as how_many_bought
FROM how_many_per_month
WHERE year = 2012
AND month BETWEEN 1 AND 3
GROUP BY name_of_product
-- NOTE: Not specifying an year will result in including all "months"
which are between the values 1 and 3 for all years. Remove it
in case you need that effect.
In the example above the database will sum all sold items between months 1 and 3 (including) for 2012. When you execute this query in your application just change the range in the BETWEEN X AND X and you'll be good to go.
Additional tip:
Avoid using sub-queries or try using the as a last resort method (in case there's simply no other way to do it). They are significantly slower than normal and even join queries. Usually sub-queries can be transformed into a join query.
SELECT
hmpm.name_of_product , SUM(hmpm.how_many_bought)
FROM (
SELECT name_of_product
FROM how_many_per_month
/* WHERE ... */
/* ORDER BY ... */
) sub
INNER JOIN how_many_per_month hmpm
ON hmpm.name_of_product = sub.name_of_product
GROUP BY hmpm.name_of_product
/* LIMIT ... */
MySQL not support LIMIT in subquery, but you need ordering and condition. And why not have id_of_product field?
I have a cron script that writes the total number of active users to a table every day. I'm trying to now generate a simple report that would show the "high water mark" for each month. Because some accounts expire during the month it's possible the highest number may NOT be at the end of the month.
Here's a sample of my table structure
tblUserLog
-----------
record_id INT(11) // PRIMARY KEY
run_date DATE // DATE RUN
ttl_count INT(11) // TOTAL FOR DAY
Sample data:
record_id run_date ttl_count
1 2013-06-01 500
2 2013-06-10 510
3 2013-06-20 520
4 2013-06-30 515
5 2013-07-01 525
6 2013-07-10 530
7 2013-07-20 540
8 2013-07-31 550
9 2013-08-01 560
What I would like returned is:
record_id run_date ttl_count
3 2013-06-20 520
8 2013-07-31 550
9 2013-08-01 560
I've tried two queries that are close...
// This will give me the total for the first of the month
SELECT s.record_id, s.run_date, s.ttl_count
FROM tblStatsIndividual s
JOIN (
SELECT record_id
FROM tblStatsIndividual
GROUP BY DATE_FORMAT(run_date, '%Y %m')
HAVING MAX(ttl_count)
) s2
ON s2.record_id = s.record_id
ORDER BY run_date DESC
This returns the total for the first of each month, along with the record_id and correct date for the total.
Tried this...
SELECT record_id,max(run_date), max(ttl)
FROM (
SELECT record_id,run_date, max(ttl_count) AS ttl
FROM tblStatsIndividual
GROUP BY DATE_FORMAT(run_date, '%Y %m')
) a
GROUP BY DATE_FORMAT(run_date, '%Y %m')
ORDER BY run_date DESC
This one appears to get the correct "high water mark" but it's not returning the record_id, or the run_date for the row that IS the high water mark.
How do you get the record_id and the run_date for the highest total?
Something like
Select detail.Record_ID, detail.Run_Date, detail.ttl_Count
From tblStatsIndividual detail
Inner Join
(Select Year(run_date) as Year, Month(Run_date) as Month, Max(ttl_count) as ttl
From tblStatsIndividual
Group By Year(run_date), Month(Run_date)) maximums
On maximums.Year = Year(detail.Run_date) and maximums.Month = Month(detail.Run_date)
and maximums.ttl = detail.ttl_count
Should do it. NB based on your requirement if you had two records in the same month with the same (and highest in the month) ttl_count, they would both be returned.
Based on the help from #Tony Hopkinson, This query gets me the info. The one caveat is it shows the ID and date for the first occurrence of the MAX total, so if the total is the same three days in a row on a month, the first day's ID is returned. For my purpose, the last ID would be more ideal, but I can live with this:
SELECT s.Record_ID, s.Run_Date, s.ttl_Count
FROM tblStatsIndividual s
INNER JOIN (
SELECT YEAR(run_date) AS yr, MONTH(run_date) AS mon, MAX(ttl_count) AS ttl
FROM tblStatsIndividual
GROUP BY DATE_FORMAT(run_date, '%Y %m')
) maximums
ON maximums.yr = YEAR(s.run_date)
AND maximums.mon = MONTH(s.run_date)
AND maximums.ttl = s.ttl_Count
GROUP BY ttl_count
ORDER BY run_date DESC
I'm trying to make a query to get rush hours for everyday on a specific month.
The table I have looks like this:
id idproduct created_at
1 021354684 2011-10-01 20:25:48
2 033546835 2011-10-01 20:30:15
3 055965654 2011-10-01 20:45:20
4 012975343 2011-10-02 14:03:36
5 021354684 2011-10-02 15:55:48
6 033546835 2011-10-02 16:30:15
7 055965654 2011-10-02 16:45:20
8 012975343 2011-10-02 18:53:36
9 021354684 2011-10-03 08:55:48
10 033546835 2011-10-03 09:30:15
11 055965654 2011-10-03 14:03:20
12 012975343 2011-10-03 14:03:36
What I try to get is something like this...:
day rush_hour number_of_rows
1 20:00 3
2 16:00 5
3 14:00 4
Is it possible to get a table like this? can you guys help me?
I made a mistake, sorry for this. The number of rows should be the total of items sold that day, not in that hour :( sorry.
http://sqlfiddle.com/#!2/5b87b/7
First, count every day's every hour's count (into a view, because we will use it twice below):
CREATE VIEW hours AS
SELECT
DATE( created_at ) AS d,
HOUR( created_at ) AS h,
COUNT(*) AS c
FROM item
GROUP BY DATE(created_at), HOUR(created_at);
Final query:
SELECT
hours.d AS `day`,
hours.h AS `rush_hour`,
hours.c AS `count`
-- get the max count for every day
FROM (
SELECT
d, -- the day
MAX(c) as c -- the count
FROM hours
GROUP BY d
) AS maxc
-- find the actual hour(s) with the max count for every day:
INNER JOIN hours ON hours.c = maxc.c
AND hours.d = maxc.d;
You're going to want to look at the MySQL Date Functions, they offer you some help with this
SELECT
day(created_at) as day,
hour(created_at) as rush_hour,
count(1) as num_rows
FROM item
GROUP BY
day(created_at), hour(created_at)
http://sqlfiddle.com/#!2/62a15/2/0
Try this:
SELECT dayofyear(created_at) as day, hour(created_at) as rush_hour, count(*) as number_of_rows
FROM table
GROUP BY dayofyear(created_at), hour(created_at);
Here it is without making a view:
SELECT ddd.day, eee.rush_hour, ddd.maxo
FROM
(select day, max(num_rows) as maxo from (
SELECT
day(created_at) as day,
hour(created_at) as rush_hour,
count(1) as num_rows
FROM item
GROUP BY
day(created_at), hour(created_at)
) as groupo group by day) as ddd
LEFT JOIN
(SELECT
day(created_at) as day,
hour(created_at) as rush_hour,
count(1) as num_rows
FROM item
GROUP BY
day(created_at), hour(created_at)
) as eee on ddd.day=eee.day and ddd.maxo=eee.num_rows
I could imagine it being formatted more nicely or having more relevant aliases, but there's just so much subselecting going on here.
And thanks SQLfiddlers for putting the data there.
And I think that if you have two hours tied for the highest number of whatever it is you are counting, they both will show up, so you'll get two (or more) records returned for that day of the month.