7

I trying to get the Grape count from dates March 1 - 3.

enter image description here

You will notice that on March 2 - there are no grapes inserted..

I'st possible to show a query from dates March 1, 2 and 3 but showing 0 count for March 2 enter image description here

In this image above only shows dates where there are grapes..

Here is mySQL query

SELECT  `fruitDate` ,  `fruitName` , COUNT( * ) 
FROM  `tbl_fruits` 
WHERE  `fruitName` =  "Grapes"
GROUP BY  `fruitDate

UPDATE 2:

Using this query:

SELECT f.fruitDate, f.fruitName, f1.count FROM tbl_fruits f
    LEFT JOIN (SELECT fruitDate, COUNT(*) as count from tbl_fruits d WHERE d.fruitName='Grapes' GROUP BY d.fruitDate) as f1 ON (f.fruitDate = f1.fruitDate) 
    GROUP BY f.fruitDate

I got this result..but its dsplaying diffrent fruit..something wrong with my query?

enter image description here

Mosty Mostacho
  • 42,742
  • 16
  • 96
  • 123
Tristan Jade Awat
  • 275
  • 2
  • 4
  • 16

2 Answers2

6

March 2 is data that does not exist in your table, what would it select from? That is to say, since count() is counting the number of rows that exist for "Grapes" on each date, and no rows exist for "Grapes" on March 2, count() has no data to count, nothing at all to tell the database to interpolate missing dates.

In order to solve this, what I have done in the past is create a separate table, Calendar, that contains a row for each date for a given range. Then, you can JOIN to this table to assure you select a row for each date. It might look something like this:

SELECT cal.`Date`, 'Grapes' as `fruitName`, COUNT(f.`fruitName`)
FROM `tbl_Calendar` cal 
LEFT JOIN `tbl_fruits` f ON cal.`Date` = f.`fruitDate`  
WHERE `fruitName` = "Grapes"
 AND '2012-03-01' <= cal.`Date` AND cal.`Date` <= '2012-03-03' 
GROUP BY cal.`Date`

Note that count(*) would never return 0 because a row would be returned for each date. To get a 0, count a field that would be NULL when 0 rows are found, in this case, I count fruitName

The Calendar table could be as simple as

CREATE TABLE tbl_Calendar (
  `Date` date NOT NULL PRIMARY KEY
)

Which you would fill with a simple PHP loop from a chosen start date to end date. You may find a benefit in adding other columns to cache things like day-of-week or holidays, but that is not needed for this task.

EDIT

In your edit, you seem to be trying to join back to your fruits table to get dates, but you have some errors in your query, try instead to substitute a similar subquery in place of my Calendar table:

SELECT cal.`Date`, 'Grapes' as `fruitName`, COUNT(f.`fruitName`)
FROM (SELECT `fruitDate` as `Date` FROM `tbl_fruits` GROUP BY `fruitDate`) cal 
LEFT JOIN `tbl_fruits` f ON cal.`Date` = f.`fruitDate`  
WHERE `fruitName` = "Grapes"
GROUP BY cal.`Date`

Note, though, while this will fill in dates missing for Grapes that are not missing for some other fruits, it will not fill in dates which are missing for all fruits.

Umbrella
  • 4,733
  • 2
  • 22
  • 31
  • can you tell me the table name and fields you made on this example? I wanna replicate this to use this code.. – Tristan Jade Awat Mar 04 '12 at 05:26
  • I'm not sure what you mean, perhaps the trivial CREATE will help? – Umbrella Mar 04 '12 at 05:32
  • THanks for you help umbrella..I was a bit confused on what you said that "March 2 is data that does not exist in your table," I have my tbl_fruits table on the 1st screentshot on my question. The result from my query is the 2nd screenshot which shows March 1 and 3 only. My query is the 3rd screenshot..:) – Tristan Jade Awat Mar 04 '12 at 05:40
  • I guess this comes down to opinion, but I feel it would be a better solution in terms of keeping the schema clean to insert the missing days in application code rather than at the data layer. – Corbin Mar 04 '12 at 05:53
  • I appreciate all you kind help. I tried this query: SELECT f.fruitDate, f.fruitName, f1.count FROM tbl_fruits f LEFT JOIN (SELECT fruitDate, COUNT(*) as count from tbl_fruits d WHERE d.fruitName='Grapes' GROUP BY d.fruitDate) as f1 ON (f.fruitDate = f1.fruitDate) GROUP BY f.fruitDate..but i get a different fruit name..anything wrong with my query? see screenshot for upddates – Tristan Jade Awat Mar 04 '12 at 05:59
  • I have also used that approach. It is a trade off, with the former keeping the application code clean, since the data layer returns the complete/desired data. – Umbrella Mar 04 '12 at 06:02
  • But it seems like my fruit name has gone crazy..it shows apples and oranges instead of grapes..LOL – Tristan Jade Awat Mar 04 '12 at 06:06
  • Yes, your query has errors. For one, your placement of the "Grapes" test. See my edit for an other approach, and a related warning. – Umbrella Mar 04 '12 at 06:09
  • Using this query " SELECT f.fruitDate, f.fruitName, f1.count FROM tbl_fruits f LEFT JOIN (SELECT fruitDate, COUNT(*) as count from tbl_fruits d WHERE d.fruitName='Grapes' GROUP BY d.fruitDate) as f1 ON (f.fruitDate = f1.fruitDate) GROUP BY f.fruitDate " can i assign 0, instead of null? where do I insert IFNULL on this query? – Tristan Jade Awat Mar 04 '12 at 06:34
  • @SalmanA Are you referring to one of mine? Sure. It's late here, and I haven't tested these. – Umbrella Mar 04 '12 at 06:39
3

Remember there is a dynamically (and a bit ugly) solution to creating a date range that does not require creating a table:

select aDate from (
  select @maxDate - interval (a.a+(10*b.a)+(100*c.a)+(1000*d.a)) day aDate from
  (select 0 as a union all select 1 union all select 2 union all select 3
   union all select 4 union all select 5 union all select 6 union all
   select 7 union all select 8 union all select 9) a, /*10 day range*/
  (select 0 as a union all select 1 union all select 2 union all select 3
   union all select 4 union all select 5 union all select 6 union all
   select 7 union all select 8 union all select 9) b, /*100 day range*/
  (select 0 as a union all select 1 union all select 2 union all select 3
   union all select 4 union all select 5 union all select 6 union all
   select 7 union all select 8 union all select 9) c, /*1000 day range*/
  (select 0 as a union all select 1 union all select 2 union all select 3
   union all select 4 union all select 5 union all select 6 union all
   select 7 union all select 8 union all select 9) d, /*10000 day range*/
  (select @minDate := '2001-01-01', @maxDate := '2002-02-02') e
) f
where aDate between @minDate and @maxDate

Depending on the length of the date range you can reduce the amount of dynamically generated results (10000 days means over 27 years of records each representing one day) by removing tables (d, c, b and a) and removing them from the upper formula. Setting the @minDate and @maxDate variables will allow you to specify the dates between you want to filter the results.

Edit:

I see you're still looking for a solution. Try this:

select c.date, f.fruitName, count(f.fruitName = 'Grapes')
from tbl_calendar c
left join tbl_fruits f
on c.date = f.fruitDate and f.fruitName = 'Grapes'
group by c.date, f.fruitName

If you also want to filter the extra dates from the created table, use this query:

select c.date, f.fruitName, count(f.fruitName = 'Grapes')
from tbl_calendar c
left join tbl_fruits f
on c.date = f.fruitDate and f.fruitName = 'Grapes'
group by c.date, f.fruitName
having c.date between
  (select min(fruitDate) from tbl_fruits) and
  (select max(fruitDate) from tbl_fruits)
Mosty Mostacho
  • 42,742
  • 16
  • 96
  • 123