count data from multiple year and group by month - mysql

I want to make chart, here's my table;
mrp
+------------+
| date |
+------------+
| 2011-10-xx |
| 2011-12-xx |
| 2012-01-xx |
| 2012-05-xx |
| 2013-01-xx |
| 2013-02-xx |
+------------+
I want to count data from last 3 years, group by month, here's what I'm trying to achieve;
+--------+--------+--------+--------+
| quarty | 2011 | 2012 | 2013 |
+--------+--------+--------+--------+
|jan-mar | 0 | 1 | 2 |
|apr-jun | 0 | 1 | 0 |
|jul-sept| 0 | 0 | 0 |
|oct-dec | 2 | 0 | 0 |
+--------+--------+--------+--------+
I tried this;
select case when month(date) between 1 and 3 then 'Jan-Mar'
when month(date) between 4 and 6 then 'Apr-Jun'
when month(date) between 7 and 9 then 'Jul-Sept'
else 'Oct-Dec' end 'quarty',
SUM(year(date) = 2011) AS `2011`,
SUM(year(date) = 2012) AS `2012`,
SUM(year(date) = 2013) AS `2013`
from `mrp` where year(date) >= 2011
group by 'quarty'
but somehow it only show 'Oct-Dec' in 2011, 2012, and 2013; is there any way to make it?
Note: I already find another query how to show all month but only in one year, and I can't sort it correctly, It show apr-jun first, then jan-mar, jul-sept, and oct-dec, how can I sort it correctly?

You can take advantage of the QUARTER function to do the work for you.
SELECT CASE WHEN QUARTER(`date`) = 1 THEN 'Jan-Mar'
WHEN QUARTER(`date`) = 2 THEN 'Apr-Jun'
WHEN QUARTER(`date`) = 3 THEN 'Jul-Sep'
WHEN QUARTER(`date`) = 4 THEN 'Oct-Dec'
END AS `Quarty`,
SUM(year(`date`) = 2011) AS `2011`,
SUM(year(`date`) = 2012) AS `2012`,
SUM(year(`date`) = 2013) AS `2013`
FROM `mrp`
WHERE year(`date`) >= 2011
GROUP BY QUARTER(`date`)
Live Sample

this is just from 2011, but you can add table from 2012 and 2013 by your self
select
case
when monthvalue=1 then 'Jan-Mar'
when monthvalue=2 then 'Apr-Jun'
when monthvalue=3 then 'Jul-Sep'
when monthvalue=4 then 'Oct-Dec'
end as period,
t2011.x as freq2011 from
(SELECT CEIL(MONTH(date)/3) as monthVALUE,count(*) as x
FROM mrp
where year(date)=2011
GROUP BY monthVALUE) t2011;

Related

Group Rows in group by though it contains NULL value in mysql / postgres

I have a table from where I am getting month names and some quantity measures.
Table Name = Month_Name
SELECT month_name,q1,q2 FROM month_name;
mysql> SELECT * FROM MONTH;
+------------+------+------+
| month_name | q1 | q2 |
+------------+------+------+
| January | 10 | 20 |
| March | 30 | 40 |
| March | 10 | 5 |
+------------+------+------+
Expected Output:
mysql> SELECT month_name ,SUM(q1),SUM(q2) FROM MONTH GROUP BY month_name;
+------------+---------+---------+
| month_name | sum(q1) | sum(q2) |
+------------+---------+---------+
| January | 10 | 20 |
| Febuary | 0 | 0 |
| March | 40 | 45 |
| April | 0 | 0 |
+------------+---------+---------+
Group by month will not print February and April since these 2 months are not present in base table. I do not want to use Union All since there will be performance issues with union All, Is there any other optimised approach to this.
You can use a calendar table which keeps track of all the month names which you want to appear in your report.
SELECT
m1.month_name,
SUM(q1) AS q1_sum,
SUM(q2) AS q2_sum
FROM
(
SELECT 'January' AS month_name UNION ALL
SELECT 'February' UNION ALL
SELECT 'March' UNION ALL
...
SELECT 'December'
) m1
LEFT JOIN month m2
ON m1.month_name = m2.month_name
GROUP BY
m1.month_name;
Note that while this solve your immediate problem, it is still not ideal, because we don't have any easy way to sort the months. A much better table design would be to maintain a date column. The month name is easily derived from the date.

mysql sum group by month and date using a contract start and end date

I have a table full of monthly contracts. There is a monthly price, a start date, and an end date for each. I am trying to graph each month's total revenue and am wondering if it's possible to do this in one query (vs. a query for each month).
I know how to group by month and year in mysql, but this requires a more complex solution that "understands" whether to include in the sum for a given month/year based on the start and end date of the contract.
Shorthand example
| contract_id | price | start_date | end_date |
| 1 | 299 | 1546318800 (1/1/19) | 1554004800 (3/31/19) |
| 2 | 799 | 1551416400 (3/1/19) | 1559275200 (5/31/19) |
With this example, there's an overlap in March. Both contracts are running in March, so the sum returned for that month should be 1098.
I'd like to be able to produce a report that includes every month between two dates, so in this case I'd send 1/1/19 - 12/31/19, the full year of 2019 and would hope to see 0 results as well.
| month | year | price_sum |
| 1 | 2019 | 299 |
| 2 | 2019 | 299 |
| 3 | 2019 | 1098 |
| 4 | 2019 | 799 |
| 5 | 2019 | 799 |
| 6 | 2019 | 0 |
| 7 | 2019 | 0 |
| 8 | 2019 | 0 |
| 9 | 2019 | 0 |
| 10 | 2019 | 0 |
| 11 | 2019 | 0 |
| 12 | 2019 | 0 |
Here is a full working script for your problem, which uses a calendar table approach to represent every month in 2019. Specifically, we represent each month using the first of that month. Then, a given price from your table is applicable to that month if there is overlap with the start and end range.
WITH yourTable AS (
SELECT 1 AS contract_id, 299 AS price, '2019-01-01' AS start_date, '2019-03-31' AS end_date UNION ALL
SELECT 2, 799, '2019-03-01', '2019-05-31'
),
dates AS (
SELECT '2019-01-01' AS dt UNION ALL
SELECT '2019-02-01' UNION ALL
SELECT '2019-03-01' UNION ALL
SELECT '2019-04-01' UNION ALL
SELECT '2019-05-01' UNION ALL
SELECT '2019-06-01' UNION ALL
SELECT '2019-07-01' UNION ALL
SELECT '2019-08-01' UNION ALL
SELECT '2019-09-01' UNION ALL
SELECT '2019-10-01' UNION ALL
SELECT '2019-11-01' UNION ALL
SELECT '2019-12-01'
)
SELECT
d.dt,
SUM(t.price) AS price_sum
FROM dates d
LEFT JOIN yourTable t
ON d.dt < t.end_date
AND DATE_ADD(d.dt, INTERVAL 1 MONTH) > t.start_date
GROUP BY
d.dt;
Demo
Notes:
If your dates are actually stored as UNIX timestamps, then just call FROM_UNIXTIME(your_date) to convert them to dates, and use the same approach I gave above.
I had to use the overlapping date range formula here, because the criteria for overlap in a given month is that the range of that month intersects the range given by a start and end date. Have a look at this SO question for more information on that.
My code is for MySQL 8+, though in practice you may wish to create a bona fide calendar table (the CTE version of which I called dates above), which contains the range of months/years which you want to cover your data set.
I understand that you will be given a range of dates for which you will need to report. My solution requires you to initialize a temporary table, such as date_table with the first day of each month for which you want to report on:
create temporary table date_table (
d date,
primary key(d)
);
set #start_date = '2019-01-01';
set #end_date = '2019-12-01';
set #months = -1;
insert into date_table(d)
select DATE_FORMAT(date_range,'%Y-%c-%d') AS result_date from (
select (date_add(#start_date, INTERVAL (#months := #months +1 ) month)) as date_range
from mysql.help_topic a limit 0,1000) a
where a.date_range between #start_date and last_day(#end_date);
Then this should do it:
select month(dt.d) as month, year(dt.d) as year, ifnull(sum(c.price), 0) as price_sum
from date_table dt left join contract c on
dt.d >= date(from_unixtime(c.start_date)) and dt.d < date(from_unixtime(c.end_date))
group by dt.d
order by dt.d
;
Resulting in:
+-------+------+-----------+
| month | year | price_sum |
+-------+------+-----------+
| 1 | 2019 | 299 |
| 2 | 2019 | 299 |
| 3 | 2019 | 1098 |
| 4 | 2019 | 799 |
| 5 | 2019 | 799 |
| 6 | 2019 | 0 |
| 7 | 2019 | 0 |
| 8 | 2019 | 0 |
| 9 | 2019 | 0 |
| 10 | 2019 | 0 |
| 11 | 2019 | 0 |
| 12 | 2019 | 0 |
+-------+------+-----------+
See demo
I am not sure about the semantics of the column end_date. Right now I am comparing the first a follows: start_date <= first_of_month < end_date. Perhaps the test should be start_date <= first_of_month <= end_date, in which case:
dt.d >= date(from_unixtime(c.start_date)) and dt.d < date(from_unixtime(c.end_date))
becomes:
dt.d between date(from_unixtime(c.start_date)) and date(from_unixtime(c.end_date))
With end_date being the last day of the month, it would not matter either way.

How to write this query MYSQL

I have this database:
| id | name | email | control_number | created | | | | | |
|:--:|-------|-----------------|----------------|------------|---|---|---|---|---|
| 1 | john | john#gmail.com | 1 | 14/09/2016 | | | | | |
| 2 | carl | carl#gmail.com | 1 | 13/08/2016 | | | | | |
| 3 | frank | frank#gmail.com | 2 | 12/08/2016 | | | | | |
And i want to get the COUNT in the last 12 months by the control_number.
basicly is a COUNT where control_number = 1 but by month.
So if the query is done today, its september, it should start from september to October 2015 and display the count of records for each month.
Result should be:
09/2016 = 50
08/2016 = 35
07/2016 = 20
06/2016 = 50
05/2016 = 21
04/2016 = 33
03/2016 = 60
02/2016 = 36
01/2016 = 11
12/2015 = 0
11/2015 = 0
10/2015 = 0
Hmmm. Getting the 0 values can be tricky. Assuming that you have some data each month (even if not for "1"), th en you can do:
select extract(year_month from created) as yyyymm,
sum(control_number = 1)
from t
where created >= date_sub(curdate(), interval 12 month)
group by extract(year_month from created)
order by yyyymm;
If you don't have at least one record for each month, then you'll need a left join and a table with one row per month.
Try this:
select CONCAT(SUBSTRING(ym, 5, 2), '/', SUBSTRING(ym, 1, 4)) Month, Count from (
select EXTRACT(YEAR_MONTH FROM created) ym, count(*) Count
from mytable
where EXTRACT(YEAR_MONTH FROM created) > (EXTRACT(YEAR_MONTH FROM SUBDATE(NOW(), INTERVAL 1 YEAR))
group by 1
order by 1 desc) x
Try:
select concat(month(created),'/',year(created)) as period, count(*) as cnt
from mytable
where control_number=1 and TIMESTAMPDIFF(year, created, now())=0
group by (month(created));

sql query to count twitter comments by month in 2016

I want to list the number of Tweets made according to month in 2016. I am new to SQL but have tried different ways to do this. Below is my latest attempt. I keep getting a message that I am not using datelogged properly. Lastly, I am not sure of how to format Total and Tweet_Cnt.
The format of the date in Twitter is as follows: MESSAGE_POSTED_TIME: 2015-08-06 21:48:34. FYI---- Column Name=MESSAGE_POSTED_TIME; Table Name=DTrumpCampaign_Tweets
Select
Year(DATELOGGED),
Sum(Case When Month(DATELOGGED) = 1 Then 1 Else 0 End) Jan,
Sum(Case When Month(DATELOGGED) = 2 Then 1 Else 0 End) Feb,
Sum(Case When Month(DATELOGGED) = 3 Then 1 Else 0 End) Mar
From
DTrumpCampaign_Tweets
Group By
Year(DATELOGGED);
I would like for my table's format to look like this
Month(2016) Tweet_Cnt
Jan 25
Feb 100
Mar 200
total 325
I greatly appreciate your help.
Thanks.
Assuming that you only want data of year 2016 only.
SELECT
(CASE WHEN t.`month` IS NULL THEN 'total'ELSE t.monthName END) AS 'Month(2016)',
t.Tweet_Cnt
FROM
(
SELECT
MONTHNAME(DATELOGGED) AS monthName,
YEAR (DATELOGGED) `year`,
MONTH (DATELOGGED) `month`,
COUNT(*) Tweet_Cnt
FROM DTrumpCampaign_Tweets
WHERE YEAR (DATELOGGED) = '2016'
GROUP BY `year`,`month` WITH ROLLUP
LIMIT 13
) t;
Demo with some sample data
You will get an output structure like below:
| Month(2016) | Tweet_Cnt |
|-------------|-----------|
| January | 1 |
| February | 2 |
| March | 1 |
| April | 1 |
| May | 1 |
| June | 1 |
| July | 1 |
| August | 1 |
| September | 1 |
| October | 1 |
| November | 1 |
| December | 1 |
| total | 13 |
More:
If you want the month names having only first three letters only then just change the corresponding line in the above query as below:
Change this line : SELECT MONTHNAME(DATELOGGED) AS monthName,
To this: SELECT DATE_FORMAT(DATELOGGED,"%b") AS monthName,
Demo of this modified query
Here is a solution, with a sqlfiddle demo:
http://sqlfiddle.com/#!9/786acb/5
SELECT
COALESCE(`Month(2016)`, 'Total') AS `Month(2016)`, Tweet_cnt
FROM
(
Select
DATE_FORMAT(datelogged, '%b') AS `Month(2016)`,
COUNT(*) AS Tweet_cnt
From dtrumpcampaign_tweets
WHERE YEAR(datelogged) = '2016'
Group BY `Month(2016)` WITH ROLLUP
) t;
The output is like:
+-------------+-----------+
| Month(2016) | Tweet_cnt |
+-------------+-----------+
| Feb | 1 |
| Jan | 2 |
| Mar | 3 |
| Total | 6 |
+-------------+-----------+
4 rows in set (0.00 sec)

How to identify entities which have repeated values in sequence using MySQL?

I have a table:
UNIT_ID | YEAR | MONTH | VAR
---------+------+-------+------
1 | 2015 | 1 | 0
1 | 2015 | 2 | 0
1 | 2015 | 3 | 0
2 | 2015 | 1 | 10
2 | 2015 | 2 | 10
2 | 2015 | 3 | 10
1 | 2015 | 4 | 5
1 | 2015 | 5 | 5
1 | 2015 | 6 | 5
2 | 2015 | 4 | 10
2 | 2015 | 5 | 3
2 | 2015 | 6 | 3
3 | 2016 | 1 | 3
3 | 2016 | 2 | 3
3 | 2016 | 3 | 3
3 | 2016 | 4 | 3
2 | 2016 | 6 | 0
2 | 2016 | 7 | 0
2 | 2016 | 8 | 0
I want to know which units have a sequence bigger than 3 zeros or bigger than 4 values repeated. Grouped by year. So, my result table would be like this:
1 | 2015 | true
2 | 2015 | true
2 | 2016 | true
I have found this solution but unfortunately I could not adapt to my case. I need also that the query is in MySQL.
You could just join them 4 times. Last join is a left join to allow the case for 3 0's.
select a.unit_id, a.year, 'true'
from tbl a
join tbl b on a.unit_id = b.unit_id and a.year = b.year and a.month+1 = b.month and a.var = b.var
join tbl c on b.unit_id = c.unit_id and b.year = c.year and b.month+1 = c.month and b.var = c.var
left join tbl d on c.unit_id = d.unit_id and c.year = d.year and c.month+1 = d.month and c.var = d.var
where a.var = 0 or d.var is not null;
Faster and more generic solution. It scans the table only once, and uses user defined variables (#pu for previous unit_id, #py for previous year, etc) to remember the previous row:
select distinct unit_id, year
from (
select unit_id, `year`, `month`, `var`,
if(unit_id=#pu and `year`=#py and `month`=#pm+1 and `var`=#pv, #i:=#i+1, #i:=1)*
if(#pu:=unit_id,1,1)*if(#py:=`year`,1,1)*if(#pm:=`month`,1,1)*if(#pv:=`var`,1,1) as c
from table1 a
join (select #pu:=null, #py:=null, #pm:=null, #pv:=null, #i:=1) b
order by unit_id, `year`, `month`, `var`) a
group by unit_id, `year`, `var`
having (`var` = 0 and max(c) >= 3) or (`var` != 0 and max(c) >= 4);
fiddle