2

I have two tables user_profile and tracked_search. The user_profile table has user details and tracked_search tracks searches made by each user.

Whenever a user makes a search this search entry goes in the tracked_search table. If nothing is searched for a particular date nothing is added in tracked_search.

I need to develop a report where in I need to show on all days of month how many users made searches.

For example:

CREATE TABLE tracked_search (
    id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
    created DATE,
    user_id int NOT NULL
);

INSERT INTO tracked_search(created, user_id) VALUES
('2017-10-01', 1000),
('2017-10-01', 1000),
('2017-10-01', 2000),
('2017-10-01', 3000),
('2017-10-01', 4000),
('2017-10-04', 1000),
('2017-10-04', 2000),
('2017-10-04', 2000),
('2017-10-04', 2000),
('2017-10-04', 2000),
('2017-10-04', 3000),
('2017-10-31', 1000),
('2017-10-31', 2000),
('2017-10-31', 3000),
('2017-10-31', 4000),
('2017-10-31', 5000);

Desired output:

Date       user_count 
2017-10-01    4
2017-10-02    0
2017-10-03    0
2017-10-04    3
2017-10-05    0
...
2017-10-30    0
2017-10-31    5

I have written following query

SELECT ts.created , count( distinct ts.user_id) FROM tracked_search ts, user_profile u
 WHERE ts.created>=(CURDATE()-INTERVAL 1 MONTH) AND u.id = ts.user_id
 group by ts.created;

but i get

Date       user_count 
2017-10-01    4
2017-10-04    3
2017-10-31    5

I need to print all days values if no entry is there for a particular date it should be zero.

I am using MySQL.

Wodin
  • 3,243
  • 1
  • 26
  • 55
Suyash
  • 331
  • 1
  • 4
  • 20
  • Issues of data display are generally best resolved in application code, if that’s available. – Strawberry Oct 29 '17 at 12:30
  • but i need to send data of each day. In java i will have to do a check to see which dates are not present and then insert those dates with zero as count in the list. this process can be avoided. – Suyash Oct 31 '17 at 10:49

3 Answers3

1

By the way, you don't need the join on user_profile.

If you have a dates table with the relevant dates, this is pretty easy:

SELECT dates.day AS `Date`, COUNT(DISTINCT ts.user_id) AS user_count
FROM dates
LEFT OUTER JOIN tracked_search AS ts
    ON ts.created = dates.day
GROUP BY dates.day;

Since you probably don't have a dates table and might not want to create and maintain one, you could use one of the solutions for generating the list of dates on the fly. e.g. Get a list of dates between two dates or How to get list of dates between two dates in mysql select query

SELECT dates.day AS `Date`, COUNT(DISTINCT ts.user_id) AS user_count
FROM (
    SELECT ADDDATE('1970-01-01', t4.i * 10000 + t3.i * 1000 + t2.i * 100 + t1.i * 10 + t0.i) AS day
    FROM (SELECT 0 AS i 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) AS t0,
         (SELECT 0 AS i 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) AS t1,
         (SELECT 0 AS i 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) AS t2,
         (SELECT 0 AS i 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) AS t3,
         (SELECT 0 AS i 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) AS t4
) AS dates
LEFT OUTER JOIN tracked_search AS ts
    ON ts.created = dates.day
WHERE dates.day >= '2017-10-01'
AND dates.day < '2017-11-01'
GROUP BY dates.day;
Wodin
  • 3,243
  • 1
  • 26
  • 55
  • Hi Wodin thanks for the reply. what if the same thing i want to group by week. requirements have changed. i have created a new question on following link https://stackoverflow.com/questions/47013616/mysql-query-to-get-sum-of-user-and-group-by-week – Suyash Oct 30 '17 at 11:04
  • @Suyash I have answered your other question based on the above answer. – Wodin Oct 30 '17 at 21:14
0

You need to write without AND u.id = ts.user_id.

SELECT ts.created , count( distinct ts.user_id) FROM tracked_search ts, user_profile u
 WHERE ts.created>=(CURDATE()-INTERVAL 1 MONTH) 
 group by ts.created;
Mike Lischke
  • 48,925
  • 16
  • 119
  • 181
  • if a date entry is not present in db it should come in result as 0. please consider the example i have provided. Thanks for the help though, – Suyash Oct 31 '17 at 10:50
0

I was able to solve this using the following logic hope it will help someone

select 
t1.attempt_date,
coalesce(SUM(t1.attempt_count+t2.attempt_count), 0) AS attempt_count
from
(
  select DATE_FORMAT(a.Date,'%Y/%m/%d') as attempt_date,
  '0' as  attempt_count
  from (
    select curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY as Date
    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) as a
    cross join (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) as b
    cross join (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) as c
  ) a
  where a.Date BETWEEN NOW() - INTERVAL 1 MONTH AND NOW()
)t1
left join
(
  SELECT DATE_FORMAT(ts.created,'%Y/%m/%d') AS attempt_date, 
  count( distinct ts.user_id) AS attempt_count
  FROM tracked_search ts, user_profile u
  WHERE ts.user_id = u.id and
  DATE_SUB(ts.created, INTERVAL 1 DAY) > DATE_SUB(DATE(NOW()), INTERVAL 1 MONTH) 
  GROUP BY DAY(ts.created) DESC
)t2
on t2.attempt_date = t1.attempt_date
group by DAY(t1.attempt_date)
order by t1.attempt_date desc;
Suyash
  • 331
  • 1
  • 4
  • 20