MySQL Count Including 0 - mysql

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

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 to select month-wise record even if data not exist

I wrote a query to get month-wise record in user table as follows
SELECT COUNT( `userID` ) AS total, DATE_FORMAT( `userRegistredDate` , '%b' ) AS
MONTH , YEAR( `userRegistredDate` ) AS year
FROM `users`
GROUP BY DATE_FORMAT( FROM_UNIXTIME( `userRegistredDate` , '%b' ) )
Output:
total MONTH year
---------------------------
3 May 2013
2 Jul 2013
--------------------------
Expected Output:
total MONTH year
---------------------------
0 Jan 2013
0 Feb 2013
0 Mar 2013
0 Apr 2013
3 May 2013
0 Jun 2013
2 Jul 2013
--------------------------
I need to show the record even if data not exist. How to do this?
I won't say much about efficiency as I have not tested it against other methods but without having a temp table this seem a fair way to go.
SELECT COUNT(u.userID) AS total, m.month
FROM (
SELECT 'Jan' AS MONTH
UNION SELECT 'Feb' AS MONTH
UNION SELECT 'Mar' AS MONTH
UNION SELECT 'Apr' AS MONTH
UNION SELECT 'May' AS MONTH
UNION SELECT 'Jun' AS MONTH
UNION SELECT 'Jul' AS MONTH
UNION SELECT 'Aug' AS MONTH
UNION SELECT 'Sep' AS MONTH
UNION SELECT 'Oct' AS MONTH
UNION SELECT 'Nov' AS MONTH
UNION SELECT 'Dec' AS MONTH
) AS m
LEFT JOIN users u
ON MONTH(STR_TO_DATE(CONCAT(m.month, ' 2013'),'%M %Y')) = MONTH(u.userRegistredDate)
AND YEAR(u.userRegistredDate) = '2013'
GROUP BY m.month
ORDER BY 1+1;
If you make the union based on a date format you can even reduce the work and load on the query.
SELECT COUNT(u.userID) AS total, DATE_FORMAT(merge_date,'%b') AS month, YEAR(m.merge_date) AS year
FROM (
SELECT '2013-01-01' AS merge_date
UNION SELECT '2013-02-01' AS merge_date
UNION SELECT '2013-03-01' AS merge_date
UNION SELECT '2013-04-01' AS merge_date
UNION SELECT '2013-05-01' AS merge_date
UNION SELECT '2013-06-01' AS merge_date
UNION SELECT '2013-07-01' AS merge_date
UNION SELECT '2013-08-01' AS merge_date
UNION SELECT '2013-09-01' AS merge_date
UNION SELECT '2013-10-01' AS merge_date
UNION SELECT '2013-11-01' AS merge_date
UNION SELECT '2013-12-01' AS merge_date
) AS m
LEFT JOIN users u
ON MONTH(m.merge_date) = MONTH(u.userRegistredDate)
AND YEAR(m.merge_date) = YEAR(u.userRegistredDate)
GROUP BY m.merge_date
ORDER BY 1+1;
Live DEMO of both queries.
You may need a table to hold every "month" record. A temp table can do the trick:
drop table if extists temp_months;
create temporary table temp_months
month date,
index idx_date(month);
insert into temp_months
values ('2013-01-31'), ('2013-02-28'), ...
And now, you can left join your data with this newly created temp table:
SELECT
COUNT( `userID` ) AS total,
DATE_FORMAT( m.month , '%b' ) AS
MONTH ,
YEAR( m.month ) AS year
FROM
months as m
left join `users` as u on m.month = last_day(FROM_UNIXTIME(`userRegistredDate`, '%b' )
GROUP BY
last_day(m.month);
Notice that you can put the temp table creation (and fill) in a stored procedure.
I use last_day for simplicity, but you are free to use any date in the month that you like, if you join it correctly.
Hope this helps

Retrieve records using JOIN query

I want to retrieve data of last 1 week from emp_info table on per day basis.
So I have used :
SELECT DAYNAME(timestamp), COUNT(*)
FROM `emp_info`
WHERE DATE(timestamp ) > DATE_SUB(CURDATE( ) , INTERVAL 1 WEEK )
GROUP BY DAYNAME(timestamp);
According to the query I am getting result like:
Monday 5
Thursday 7
But I also want the result of weekday as 0 on which no record has been entered.
From suggestions I come to know about JOIN query. So I have tried to fix it but not getting any solution.
The result you are getting is right because there are no records on a specific dayname. Since you want to get all daynames, you need to project complete set of day (using UNION inside a SUBQUERY) and join it with your existing query.
SELECT a.day_name,
COALESCE(b.totalCount, 0) totalCount
FROM
(
SELECT 'Sunday' day_name, 1 ordby UNION ALL
SELECT 'Monday' day_name, 2 ordby UNION ALL
SELECT 'Tuesday' day_name, 3 ordby UNION ALL
SELECT 'Wednesday' day_name, 4 ordby UNION ALL
SELECT 'Thursday' day_name, 5 ordby UNION ALL
SELECT 'Friday' day_name, 6 ordby UNION ALL
SELECT 'Saturday' day_name, 7 ordby
) a
LEFT JOIN
(
SELECT DAYNAME(timestamp) day_name,
COUNT(*) totalCount
FROM `emp_info`
WHERE DATE(timestamp ) > DATE_SUB(CURDATE( ) , INTERVAL 1 WEEK )
GROUP BY DAYNAME(timestamp)
) b ON a.day_name = b.day_name
ORDER BY a.ordby
SQLFiddle Demo (simple example)

SQL query to retrieve SUM in various DATE ranges

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)