SQL query to retrieve SUM in various DATE ranges - mysql

I have a table with information about sold products, the customer, the date of the purchase and summary of sold units.
The result I am trying to get should be 4 rows where the 1st three are for January, February and March. The last row is for the products that weren't sold in these 3 months.
Here is the table. http://imageshack.us/a/img823/8731/fmlxv.jpg
The table columns are:
id
sale_id
product_id
quantity
customer_id
payment_method_id
total_price
date
time
So in the result the 1st 3 row would be just:
January, SUM for January
February, SUM for February
March, SUM for MArch
and the next row should be for April, but there are no items in April yet, so I don't really know how to go about all this.
Editor's note: based on the linked image, the columns above would be for the year 2013.

I would go with the following
SELECT SUM(totalprice), year(date), month(date)
FROM sales
GROUP BY year(date), month(date)

This answer is based on my interpretation of this part of your question:
March, SUM for MArch and the next row should be for April, but there are no items in April yet, so I don't really know how to go about all this.
If you're trying to get all months for a year (say 2013), you need to have a placeholder for months with zero sales. This will list all the months for 2013, even when they don't have sales:
SELECT m.monthnum, SUM(mytable.totalprice)
FROM (
SELECT 1 AS monthnum, 'Jan' as monthname
UNION SELECT 2, 'Feb'
UNION SELECT 3, 'Mar'
UNION SELECT 4, 'Apr'
UNION SELECT 5, 'May'
UNION SELECT 6, 'Jun'
UNION SELECT 7, 'Jul'
UNION SELECT 8, 'Aug'
UNION SELECT 9, 'Sep'
UNION SELECT 10, 'Oct'
UNION SELECT 11, 'Nov'
UNION SELECT 12, 'Dec') m
LEFT JOIN my_table ON m.monthnum = MONTH(mytable.date)
WHERE YEAR(mytable.date) = 2013
GROUP BY m.monthnum
ORDER BY m.monthnum

If I understand what you mean, you can put logic in a case statement and use that in a group by:
select (case when month(date) = 1 then 'Jan'
when month(date) = 2 then 'Feb'
when month(date) = 3 then 'Mar'
else 'Other'
end) as period, sum(quantity) as sumquantity
from t
group by (case when month(date) = 1 then 'Jan'
when month(date) = 2 then 'Feb'
when month(date) = 3 then 'Mar'
else 'Other'
end)

Related

How to show null or zero values in cross/left join MYSQL with BETWEEN DATE condition

I have a query that count rows between dates(3 months). It shows me the total rows for each month which have records in that month. But if records in month not exists, the query isn`t showing me the null or zero value.
I've tried left join, cross join, coalesce(t2.id,0) and I'm don't achieve anything.
Here's my code:
SELECT CONCAT(t1.month, ' ' , y.years) as month, COUNT(t2.id) as quantity
FROM
( SELECT 'Jan' month UNION ALL
SELECT 'Feb' month UNION ALL
SELECT 'Mar' month UNION ALL
SELECT 'Apr' month UNION ALL
SELECT 'May' month UNION ALL
SELECT 'Jun' month UNION ALL
SELECT 'Jul' month UNION ALL
SELECT 'Aug' month UNION ALL
SELECT 'Sep' month UNION ALL
SELECT 'Oct' month UNION ALL
SELECT 'Nov' month UNION ALL
SELECT 'Dec' month ) t1 CROSS JOIN
(SELECT DISTINCT YEAR(data) as years FROM negocio) y
LEFT JOIN
(SELECT id, data FROM negocio) t2
on t1.month = LEFT(monthname(t2.data), 3) AND y.years = YEAR(t2.data)
WHERE (t2.data BETWEEN NOW() - INTERVAL 3 MONTH AND NOW())
GROUP BY t1.month, y.years ORDER BY year(STR_TO_DATE(years, '%YYYY')), month(STR_TO_DATE(month, '%M'))
What I need is described in this example:
|--------------| |--------------|
|month|quantity| |month|quantity|
|-----|--------| and it must show -> |-----|--------|
| Feb | 5 | | Feb | 5 |
| Apr | 26 | | Mar | 0 |
|--------------| | Apr | 26 |
|--------------|
Any ideas?
Your WHERE clause is what is causing you not to get any data in your output for March, as it will fail for any row in negocio that has a NULL data value, thus effectively converting the LEFT JOIN into an INNER JOIN and removing rows for any month which has no values in negocio (see the manual). That condition should be moved to the ON condition. You do still need a WHERE clause, but it should be on the date generated from t1 and y. I've assumed your requirement on the 9th of May is to count values from the 9th of February onwards, in which case the required condition is:
WHERE STR_TO_DATE(CONCAT_WS('-', y.year, t1.month, DAY(CURDATE())), '%Y-%b-%d') BETWEEN CURDATE() - INTERVAL 3 MONTH AND CURDATE()
This query should give you the results you want. For ease of ordering and joining to the negocio table, I've added a month number field to your months (t2) table:
SELECT CONCAT(t1.month, ' ' , y.year) as month,
COUNT(t2.id) as quantity
FROM
(SELECT 1 AS mnum, 'Jan' AS month UNION ALL
SELECT 2, 'Feb' UNION ALL
SELECT 3, 'Mar' UNION ALL
SELECT 4, 'Apr' UNION ALL
SELECT 5, 'May' UNION ALL
SELECT 6, 'Jun' UNION ALL
SELECT 7, 'Jul' UNION ALL
SELECT 8, 'Aug' UNION ALL
SELECT 9, 'Sep' UNION ALL
SELECT 10, 'Oct' UNION ALL
SELECT 11, 'Nov' UNION ALL
SELECT 12, 'Dec' ) t1 CROSS JOIN
(SELECT DISTINCT YEAR(data) AS year FROM negocio) y
LEFT JOIN negocio t2 ON t1.mnum = MONTH(t2.data) AND y.year = YEAR(t2.data) AND t2.data BETWEEN CURDATE() - INTERVAL 3 MONTH AND CURDATE()
WHERE STR_TO_DATE(CONCAT_WS('-', y.year, t1.month, DAY(CURDATE())), '%Y-%b-%d') BETWEEN CURDATE() - INTERVAL 3 MONTH AND CURDATE()
GROUP BY y.year, t1.month, t1.mnum
ORDER BY y.year, t1.mnum
Demo on dbfiddle

Sql Command to sort data as it is stored

select DISTINCT Month, Code
from SalaryRecord
order by Code ASC
The result is
Feb
Jan
Jan
but I want Jan and Feb arranged by month ascending order
I assume the Month column is a string type (VARCHAR(n)), so it's being ordered as a string - and the ordering is perfectly correct (F comes before J in Western European alphabets) in that case.
Do you have the month as a number in your table, too?
If so, you could order by that numerical value instead:
SELECT DISTINCT
Month, Code
FROM
dbo.SalaryRecord
ORDER BY
NumericalMonth, Code ASC
Or maybe you have a DATE (or DATETIME, DATETIME2 column?) and you could order by that:
SELECT DISTINCT
Month, Code
FROM
dbo.SalaryRecord
ORDER BY
MONTH(YourDateColumn), Code ASC
And if you have nothing at all your can easily sort on - well, then you'll need this big ugly CASE:
SELECT DISTINCT
Month, Code
FROM
dbo.SalaryRecord
ORDER BY
CASE MONTH
WHEN 'Jan' THEN 1
WHEN 'Feb' THEN 2
WHEN 'Mar' THEN 3
WHEN 'Apr' THEN 4
WHEN 'May' THEN 5
WHEN 'Jun' THEN 6
WHEN 'Jul' THEN 7
WHEN 'Aug' THEN 8
WHEN 'Sep' THEN 9
WHEN 'Oct' THEN 10
WHEN 'Nov' THEN 11
WHEN 'Dec' THEN 12
ELSE 999
END
Try this code,
select DISTINCT Month, Code
from SalaryRecord
order by DATEPART(mm,CAST([Month]+ ' 1900' AS DATETIME)) asc

MySQL Count Including 0

I'm trying to get a total count of entries by month, over the course of several months. Here is the query I'm using:
SELECT COUNT(siteCount) AS Hits, DATE_FORMAT(TIMESTAMP,'%b') AS Month
FROM count
WHERE redirectDomain LIKE 'http://domain.com'
AND DATE_FORMAT(TIMESTAMP,'%Y') = '2013'
AND DATE_FORMAT(TIMESTAMP,'%b') BETWEEN 'Sep' AND 'Nov'
GROUP BY DATE_FORMAT(TIMESTAMP,'%b')
This is working great except when the count is 0. The month of Nov has 0 entries so it does not show in the results. How can I get Nov included in the results and showing a count of 0?
I'm guessing I need either ISNULL or CASE, but I've tried both and cannot figure out how to make this work.
Thanks for the help.
You're correct, it's not returning your month field because there aren't any rows (which is why your count is zero) - you could replace your count with a subselect which will keep them seperate from grouping etc:
SELECT
(SELECT
COUNT(c2.siteCount)
FROM
count c2
WHERE
redirectDomain
LIKE
'http://domain.com'
AND c2.TIMESTAMP = c1.TIMESTAMP) as Hits,
DATE_FORMAT(TIMESTAMP, '%b') AS Month,
FROM count c1
-- ...
I have a feeling you shouldn't be using TIMESTAMP either... reserved words...?
I have solved your problem,just follow this-
I created a table..
create table test(month text,site_count int null);
insert into test values('Sept',1),('Oct',1);
insert into test(month) values('Nov');
I did not give any value in the site_count column for the month 'Nov' i.e. the site_count of 'Nov' is null.Now I tried to count the site_count column according to the months and tried to get '0' for the month 'Nov' as in this month there was null in the site_count column.
I tried this following query and it worked,i.e. I got '0' in the site_count column for the month 'Nov' as you needed..
select month,count(site_count) from test ;
SELECT SUM(Hits) AS Hits,Month,MonthOrder
FROM
(
SELECT COUNT(siteCount) AS Hits,
DATE_FORMAT(TIMESTAMP,'%b') AS Month,
DATE_FORMAT(TIMESTAMP,'%m') AS MonthOrder
FROM count
WHERE redirectDomain LIKE 'http://domain.com'
AND DATE_FORMAT(TIMESTAMP,'%Y') = '2013'
AND DATE_FORMAT(TIMESTAMP,'%b') IN ('Sep','Oct','Nov')
GROUP BY DATE_FORMAT(TIMESTAMP,'%b')
UNION SELECT 0 AS Hits, 'Jan' AS Month,'01' AS MonthOrder
UNION SELECT 0 AS Hits, 'Feb' AS Month,'02' AS MonthOrder
UNION SELECT 0 AS Hits, 'Mar' AS Month,'03' AS MonthOrder
UNION SELECT 0 AS Hits, 'Apr' AS Month,'04' AS MonthOrder
UNION SELECT 0 AS Hits, 'May' AS Month,'05' AS MonthOrder
UNION SELECT 0 AS Hits, 'Jun' AS Month,'06' AS MonthOrder
UNION SELECT 0 AS Hits, 'Jul' AS Month,'07' AS MonthOrder
UNION SELECT 0 AS Hits, 'Aug' AS Month,'08' AS MonthOrder
UNION SELECT 0 AS Hits, 'Sep' AS Month,'09' AS MonthOrder
UNION SELECT 0 AS Hits, 'Oct' AS Month,'10' AS MonthOrder
UNION SELECT 0 AS Hits, 'Nov' AS Month,'11' AS MonthOrder
UNION SELECT 0 AS Hits, 'Dec' AS Month,'12' AS MonthOrder
)T1
GROUP BY Month,MonthOrder
ORDER BY MonthOrder ASC
delete your checking conditions as you see fit, i don't think you need the IN ('Sep','Oct','Nov') anymore it's up to you.
Here's the sqlFiddle example
you can't just count table only. below query might help you.
UPDATED
SELECT month_tab.Mo, IF(x.Hists IS NULL, 0, x.Hists)
FROM
(
SELECT 'Jan' AS Mo
UNION
SELECT 'Feb' AS Mo
UNION
SELECT 'Jun' AS Mo
UNION
SELECT 'Aug' AS Mo
UNION
SELECT 'Sep' AS Mo
UNION
SELECT 'Oct' AS Mo
UNION
SELECT 'Nov' AS Mo
) month_tab
LEFT JOIN
(
SELECT SUM(count.siteCount) AS Hists, DATE_FORMAT(timestamp, '%b') Mo
FROM count
WHERE redirectDomain LIKE 'http://domain.com'
GROUP BY Mo
) x ON month_tab.Mo = x.Mo;
Case is certainly an option...
SELECT
CASE WHEN
COUNT(`siteCount`) = 0
THEN
'0'
ELSE
COUNT(`siteCount`)
END AS Hits,
DATE_FORMAT(TIMESTAMP,'%b') AS Month
... but not necessary in this case (see full query with notes below):
SELECT
COUNT(`siteCount`) AS Hits,
-- See below for an explanation about naming conventions... 'TIMESTAMP' is probably
-- a name you'd want to avoid as well. If you can't avoid it though, use `backticks`.
DATE_FORMAT(`TIMESTAMP`,'%b') AS Month
-- I recommend changing the name of your database to something other than 'count'
-- because that can really confuse the system and make life much harder on you.
-- It's kind of like naming your daughter 'Daughter' or creating a PHP function
-- named 'function'. In either case you're eventually going to end up with a mess.
-- Fortunately, MySQL allows you to use `backticks` which, in this case, are
-- absolutely crucial (if for no other reason than maintaining sanity). ;)
FROM `count`
WHERE `redirectDomain` LIKE 'http://domain.com'
-- This will return a 4-digit numeric year (removing 'quotes')
AND DATE_FORMAT(`TIMESTAMP`,'%Y') = 2013
-- AND DATE_FORMAT(TIMESTAMP,'%b') BETWEEN 'Sep' AND 'Nov'
-- This doesn't know that you're talking about months... All it knows is to look
-- for values between the group of letters "Sep" and the group of letters "Nov".
-- For example, 'Feb' would not be counted as being between 'Jan' and 'Mar', because
-- it starts with the letter F, while 'Jul' and 'Jun' would.
-- Instead, convert to numbers:
AND DATE_FORMAT(`TIMESTAMP`,'%c') BETWEEN 9 AND 11
GROUP BY DATE_FORMAT(`TIMESTAMP`,'%b')
-- If you want the results ordered chronologically (instead of alphabetically) you
-- can use the same logic as is written above, converting letters to numbers.
-- However THIS time, use '%m' instead of '%c' because it will add a '0' to the
-- left of the month number. (Delete the next line if that's not what you want).
ORDER BY DATE_FORMAT(`TIMESTAMP`,'%m') ASC

I want get sales for year using MySQL

I want to get following result for given year
month | sales
jan 0.00
feb 4.00
mar 0.00
apr 45.00
.
.
.
.
dec 0.00
table that i have is "sales" and it has salse_value, sales_date as columns.
question is how can get above result using case when statement in MYSQL
you need to generate all month name and join it with your tables name so months that doesn't have records will have zero value for total sales
SELECT a.MonthName, COALESCE(SUM(b.sales_value), 0) totalSales
FROM
(
SELECT 'Jan' AS monthName, 1 monthOrder
UNION
SELECT 'Feb' AS monthName, 2 monthOrder
UNION
SELECT 'Mar' AS monthName, 3 monthOrder
UNION
SELECT 'Apr' AS monthName, 4 monthOrder
UNION
SELECT 'May' AS monthName, 5 monthOrder
UNION
SELECT 'Jun' AS monthName, 6 monthOrder
UNION
SELECT 'Jul' AS monthName, 7 monthOrder
UNION
SELECT 'Aug' AS monthName, 8 monthOrder
UNION
SELECT 'Sep' AS monthName, 9 monthOrder
UNION
SELECT 'Oct' AS monthName, 10 monthOrder
UNION
SELECT 'Nov' AS monthName, 11 monthOrder
UNION
SELECT 'Dec' AS monthName, 12 monthOrder
) a LEFT JOIN sales b
ON a.monthName = b.monthName
GROUP BY a.monthName
ORDER BY a.MonthOrder ASC
SELECT MONTHNAME(date_column), SUM(sales) sales
FROM TableName
GROUP BY MONTHNAME(date_column);
If you want to get abbreviated names for month, you can try
SELECT DATE_FORMAT(sales_date, '%b') AS month, SUM(sales_value)
FROM sales
GROUP BY month;
If there is no record of a particular month result won't contain any row for that month.

GROUP BY continuous months in different years

I have one table (but at many locations):
DATE STUFF
-------------------
2011-12-01 DATA
2011-12-02 DATA
2011-12-03 DATA
...
2011-12-31 DATA
2012-01-01 DATA
2012-01-02 DATA
My table covers multiple years from 2005 to 2012. I want to get AGGREGATE Function values, i.e., SUM/AVG/MAX/MIN, for each month within each year. Easy:
GROUP BY DATE_FORMAT(DATE, '%Y'), DATE_FORMAT(DATE, '%m')
I want go do the same for 3-month time periods within those years... this works for all but one:
GROUP BY DATE_FORMAT(DATE, '%Y'), DATE_FORMAT(DATE, '%m') IN (12, 01, 02)
My other time periods work, because they are in the same year (03, 04, 05), (06, 07, 08), and (09, 10, 11)... but the GROUP BY above is grouping December of 2012 with January/February of 2012. My time period has to be December 2011 and January/February 2012, December 2010 and January/February 2011... ...
I want to keep this generic, so I don't have to update with date spans, in order to put the code in a stored procedure for multiple locations.
I've tried to join the table to it self by shifting the year ahead by one if MONTH 12. This yielded undesirable results.
GROUP BY floor((month(DATE) + year(DATE) * 12 -1 + %month_shift%) / %month_group_period%)
where %month_group_period% is three (3) in your example
and %month_shift% is one (1) to obtain december, january, february together, and so on
EDIT: this works for 5 month period too (if you want)
I'm assuming a little bit here about what you really want to do with DATE_FORMAT(DATE, '%m') IN (12, 01, 02), but:
SELECT IF(DATE_FORMAT(DATE, '%m') = 12, DATE_FORMAT(DATE, '%Y') + 1, DATE_FORMAT(DATE, '%Y')) AS yr,
CASE DATE_FORMAT(DATE, '%m')
WHEN 12 THEN 1
WHEN 1 THEN 1
WHEN 2 THEN 1
WHEN 3 THEN 2
WHEN 4 THEN 2
WHEN 5 THEN 2
WHEN 6 THEN 3
WHEN 7 THEN 3
WHEN 8 THEN 3
WHEN 9 THEN 4
WHEN 10 THEN 4
WHEN 11 THEN 4
END AS qtr
FROM ...
GROUP BY IF(DATE_FORMAT(DATE, '%m') = 12, DATE_FORMAT(DATE, '%Y') + 1, DATE_FORMAT(DATE, '%Y')),
CASE DATE_FORMAT(DATE, '%m')
WHEN 12 THEN 1
WHEN 1 THEN 1
WHEN 2 THEN 1
WHEN 3 THEN 2
WHEN 4 THEN 2
WHEN 5 THEN 2
WHEN 6 THEN 3
WHEN 7 THEN 3
WHEN 8 THEN 3
WHEN 9 THEN 4
WHEN 10 THEN 4
WHEN 11 THEN 4
END
I don't think you want to use GROUP BY how you are trying to do it. You would probably want to write your queries like this
SELECT MONTH(date) as `month`, SUM(stuff) as `sum`
FROM table
WHERE YEAR(date) IN ('2010', '2011', 2012')
GROUP BY `month`
The GROUP BY clause is not where you should be filtering/formatting your data.
Of course, you could use whatever aggregate function you want instead of SUM. Also you can omit the WHERE clause for all years, or modify the years to be included as needed.
If you want year and month, you could simply modify the query like this:
SELECT YEAR(date) as `year`, MONTH(date) as `month`, SUM(stuff) as `sum`
FROM table
WHERE `year` IN ('2010', '2011', 2012')
GROUP BY `year`, `month`
You need to subtract one month from the date for the group by:
GROUP BY DATE_FORMAT(date_add(DATE, interval 1 month), '%Y'),
DATE_FORMAT(date_add(date, interval 1 month), '%m') IN (12, 01, 02)
You will probably also have to modify the select.
select DATE_FORMAT(date_add(date_add(DATE, interval 1 month), interval -1 month), '%Y'),
DATE_FORMAT(date_add(date_add(date, interval 1 month), interval -1 month), '%m')
I would try something like this:
GROUP BY DATE_FORMAT(IF(MONTH(`DATE`)=12,DATE_ADD(`DATE`,INTERVAL 1 YEAR),`DATE`),'%Y')
, DATE_FORMAT(`DATE`,'%m')
Basically, if month is 12, then add 1 year to it.
Actually, I would tend to do it something like this:
GROUP BY DATE_FORMAT(DATE_ADD(`DATE`,INTERVAL IF(MONTH(`DATE`)=12,1,0) YEAR),'%Y')
, CASE WHEN MONTH(`DATE`) IN (12,1,2) THEN 1
WHEN MONTH(`DATE`) IN (3,4,5) THEN 2
WHEN MONTH(`DATE`) IN (6,7,8) THEN 3
WHEN MONTH(`DATE`) IN (9,10,11) THEN 4
END