4

I'd like to count the total number of purchases as well as the purchases by item_id over time. In this example, a user can own an item and these items can be purchased by other users. An owner can't purchase their own item.

The problem I'm having is how to return results with counts of "0" for days where there were no purchases alongside the days with positive integer counts.

Here's my tables:

      items           |          items_purchased          | numbers |   dates
i_id  item_id user_id | p_id item_id  user_id     date    |   num   | datefield
  1      1       11   |  1      1         13   2009-01-11 | 1       | 2005-06-07
  2      2       12   |  2      1         14   2009-01-11 | 2       | 2005-06-08
  3      3       11   |  3      2         15   2009-01-12 | 3       | 2005-06-09   
                      |  4      3         16   2009-01-12 | ...     | ...
                      |  5      1         17   2011-12-12 | 1000    | 2015-06-07

Here's my MYSQL query for the total numbers of purchases of user_id=11's items:

SELECT COUNT(*) as counts, DATE(purchase_date) as DATE
FROM items_purchased 
JOIN items on items_purchased.item_id=items.item_id 
WHERE items.user_id=11 
GROUP BY DATE(purchase_date)
//note this query **doesn't** make use of the numbers and dates tables b/c I don't know how to use them

Here's the results:

counts    date
  2    2009-01-11
  1    2009-01-12
  1    2011-12-12

Here's what I'd like to see instead:

counts    date
  2    2009-01-11
  1    2009-01-12
  0    2009-01-13
  0    ... // should be a row here for each day between 2009-01-13 and 2011-12-12
  1    2011-12-12
  0    ... // should be a row here for each day between 2011-12-12 and current date
  0    current date (2012-6-27)

Here's my MYSQL query for the total numbers of purchases restricted to item_id=1 which is owned by user_id=11:

SELECT COUNT(*) as counts, DATE(purchase_date) as DATE
FROM items_purchased 
JOIN items on items_purchased.item_id=items.item_id 
WHERE items.user_id=11 and items.item_id=1
GROUP BY DATE(purchase_date)

Here's the results:

counts    date
  2    2009-01-11
  1    2011-12-12

Similar to the above, here's what I'd like to see instead:

counts    date
  2    2009-01-11
  0    2009-01-12
  0    ... // should be a row here for each day between 2009-01-12 and 2011-12-12
  1    2011-12-12
  0    ... // should be a row here for each day between 2011-12-12 and current date
  0    current date (2012-6-27)

Somehow I think I need to incorporate the numbers and dates tables but I'm not sure how to do this. Any thoughts would be greatly appreciated,

thanks, tim

tim peterson
  • 23,653
  • 59
  • 177
  • 299
  • possible duplicate of [Retrieve missing dates from database via MySQL](http://stackoverflow.com/questions/2978129/retrieve-missing-dates-from-database-via-mysql) – Bohemian Jun 28 '12 at 02:28
  • @Bohemian thanks I've seen many of these examples on SO, I'm just having trouble understanding how to apply them to my query. I'm still learning MySQL so it is hard for me to extrapolate... – tim peterson Jun 28 '12 at 02:32

1 Answers1

4

EDITED FOR CORRECTING ANSWER:

http://sqlfiddle.com/#!2/ae665/4

SELECT date_format(datefield,'%Y-%m-%d') AS DATE, IFNULL(counts, 0), item_id FROM 
    dates a
LEFT JOIN 
    (SELECT COUNT(*) as counts, purchase_date,user_id,item_id 
     FROM items_purchased 
     WHERE item_id=1
     GROUP BY date(purchase_date),item_id )r 
ON date(a.datefield) = date(r.purchase_date) ;

The above query is based on assumption:

  1. Table dates contains sequential of dates that is within the range of date you want to list.
  2. Not really sure what is the items table for. The second query is to group by purchase_date and item_id of items_purchased table.
  3. Count is to count particular item purchased on the particular day (regardless of user_id).

UPDATE by @timpeterson (OP) Major thanks to @Sel. Here's sqlfiddles demonstrating both queries I'm interested in:

  1. Purchases/day for all items owned by a single user (e.g., user_id=11): http://sqlfiddle.com/#!2/76c00/3
  2. Purchases/day for item_id=1 which is owned by user_id=11: http://sqlfiddle.com/#!2/76c00/1

here's the SQL code for the 2nd one in case the link gets broken somehow:

SELECT date_format(datefield,'%Y-%m-%d') AS DATE, 
IFNULL(countItem, 0), item_id
FROM dates a
LEFT JOIN 
(SELECT countItem, purchase_date,i.user_id,p.item_id FROM (
   SELECT count(*) as countItem, purchase_date,user_id,item_id 
   FROM items_purchased 
   GROUP BY date(purchase_date),item_id
   ) p 
 inner join items i
 on i.item_id=p.item_id
 WHERE p.item_id='1' and i.user_id='11' //just get rid of "p.item_id='1'" to produce the 1st query result
)r 
ON date(a.datefield) = date(r.purchase_date);
tim peterson
  • 23,653
  • 59
  • 177
  • 299
sel
  • 4,982
  • 1
  • 16
  • 22
  • hi @Sel, thanks. 2 questions: 1) did you mean to say `"2009-01-10"` for both dates or should one be `"10"` and the other `"20"`? 2) where to include the `where` statement to capture `user_id=11`? Can you update your answer to comment on these? thank you! – tim peterson Jun 28 '12 at 03:35
  • -@Sel, also I can't get your query to return anything other than an empty row. `Purchase_date` is a `datetime` formatted like this, "2007-02-02 20:27:26". Does that matter? – tim peterson Jun 28 '12 at 03:56
  • Yes. the datetime does matter as the first query return resultset like "2007-02-02". – sel Jun 28 '12 at 03:57
  • hmm, so in the 2nd query I need to do DATE_FORMAT(r.purchase_date, '%Y %m %d')? – tim peterson Jun 28 '12 at 03:59
  • -@Sel, thanks this is really nice, two comments: 1) how to change days with `NULL` counts to `0` counts? 2) if I include user_id=12, then all 10 days are no longer represented: http://sqlfiddle.com/#!2/194ad/13 – tim peterson Jun 28 '12 at 04:23
  • thanks, "2009-1-10" is duplicated, also if I change "WHERE @d <= '2009-01-27')" then why aren't 17 rows returned instead of only 10? http://sqlfiddle.com/#!2/194ad/25 – tim peterson Jun 28 '12 at 04:34
  • "2009-1-10" is duplicated due to the second query result. I would suggest you work on the second query to get the correct result as i am not sure the relationship in between items and items_purchased table. For the 17 rows issue, i just found out there is limitation on the first query as it depends on number of rows of the table used in the first query. Can't solve that part for now yet. – sel Jun 28 '12 at 06:00
  • I relook into your question and found there is a date table. the data in the date table is sequential date? if it is, you could make use of that table. – sel Jun 28 '12 at 08:05
  • I have edited my answer above making assumption of Dates table contains the sequence of date. – sel Jun 28 '12 at 08:23
  • hi @Sel, thanks for editing your answer. I clarified the question above but here is it again: In this example, a user can own an item and these items can be purchased by other users. An owner can't purchase their own item. Here's a sqlfiddle with data that fits the conditions: http://sqlfiddle.com/#!2/94b1f/20. The only problem is the `counts` are doubled because of the JOIN statement in introduced in the subquery. Any thoughts on how to correct that? I really appreciate all your help! – tim peterson Jun 28 '12 at 13:53
  • -@Sel, would you mind using the sqlfiddle I made ( http://sqlfiddle.com/#!2/94b1f/20 ) to get the # of purchases by user_id=11 **AND item_id=1? – tim peterson Jun 29 '12 at 00:58
  • Referring to ( sqlfiddle.com/#!2/94b1f/20 ) , the owner table ie ITEMS have duplicate items of 1, is this correct? – sel Jun 29 '12 at 01:03
  • http://sqlfiddle.com/#!2/94b1f/40 you could try this one. See if this is what you want. – sel Jun 29 '12 at 01:08
  • -@Sel, awesome, that's it, i'll include links in your answer to both of the queries I am interested in, thanks again for all your help!!! – tim peterson Jun 29 '12 at 02:55
  • -@Sel, one more level of detail I missed: The query right now returns all days in the dates table. How to restrict the results from first purchase date to the current date (for this example let's say that is 2009-01-14)? – tim peterson Jun 29 '12 at 15:26
  • Hi @timpeterson, i have come out with an not so elegant way to achieve what you want. [sqlfiddle.com/#!2/fee5d/6](http://sqlfiddle.com/#!2/fee5d/6) If you got other solution that is more elegant, pls do share. Thanks. – sel Jul 02 '12 at 03:58
  • hi -@sel, i realized i never followed up on your last sqlfiddle, which works great btw, I also wanted to say thanks again for all the time and effort you put into helping me wit this problem. best regards, – tim peterson Aug 18 '12 at 05:54