简体   繁体   中英

Mysql select total Count of posts of a user day by day for last week

currently i'm using php/mysql and part of project i show to user his total count of posts he posted in website, count of today's posts, and count of ppl following him

For that currently im doing

SELECT
(SELECT COUNT(*) FROM posts WHERE userId='{$u->id}) as totalposts,
(SELECT COUNT(*) FROM posts WHERE userId='{$u->id} and DATE(datePosted) = DATE(NOW())) as todayposts,
(SELECT COUNT(*) FROM user_follower WHERE followedId='{$u->id}') as myfollowers");

This query give me 3 number, of Total posts, today posts and Total followers following user

the point is i want to show a bar chart next to this number that represent the user activity over past week (curve or bar chart that can tell user if he's droping of increasing etc..)

example this query now returns

totalposts|todayposts|myfollowers|
100       | 4        | 300       |

but because i also need the bar chart now presenting his post counts over days. i did

SELECT
          (SELECT COUNT(*) FROM posts WHERE userId=2) as tpT,
          (SELECT COUNT(*) FROM posts WHERE userId=2 and DATE(timePosted) <= DATE(NOW()) - INTERVAL 1 DAY ) as tp1,
          (SELECT COUNT(*) FROM posts WHERE userId=2 and DATE(timePosted) <= DATE(NOW()) - INTERVAL 2 DAY ) as tp2,
          (SELECT COUNT(*) FROM posts WHERE userId=2 and DATE(timePosted) <= DATE(NOW()) - INTERVAL 3 DAY ) as tp3,
          (SELECT COUNT(*) FROM posts WHERE userId=2 and DATE(timePosted) <= DATE(NOW()) - INTERVAL 4 DAY ) as tp4,
          (SELECT COUNT(*) FROM posts WHERE userId=2 and DATE(timePosted) <= DATE(NOW()) - INTERVAL 5 DAY ) as tp5,
          (SELECT COUNT(*) FROM posts WHERE userId=2 and DATE(timePosted) <= DATE(NOW()) - INTERVAL 6 DAY ) as tp6,
          (SELECT COUNT(*) FROM posts WHERE userId=2 and DATE(timePosted) <= DATE(NOW()) - INTERVAL 7 DAY ) as tp7,
          (SELECT COUNT(*) FROM posts WHERE userId=2 and DATE(timePosted) = DATE(NOW())) as tdvT

Result
tpT|tp1|tp2|tp3|tp4|tp5|tp6|
100|96 | 90| 90| 89|80 |64 |

in where tps(1) represent WHERE posts was posted before current date - 1 day.

so how can i do such loop in statement ? or do should i keep it like that?

currently its not causing much of a trouble. 0.0008 sec excution time so its great.

yet is there a better way to do it ?

i looks stupid :p

thanks in advance

EDIT

For future googlers.

i ended up doing

$v=$this->db->query("SELECT count(*) as count,date(timePosted) as day FROM `posts` where userId = {$u->id} group by date(timePosted)");

$pts=[];

for($i=0; $i <= 6; $i++){
    $pts[date('Y-m-d',strtotime(date('Y-m-d')."- $i Day"))]='0';
    $vs[date('Y-m-d',strtotime(date('Y-m-d')."- $i Day"))]='0';
}

foreach($v->result() as $s){
if(isset($vs[$s->day]))$vs[$s->day]=$s->count;
}

So i ended up with letting php handling it for sake of execution time.

I would alter via the sample below. By the select() SQLVars will pre-compute each of the days once before joining to the "posts" table. Now the SUM( case/when ) will only need to compare to that "@Day?" value. Since it is all for the same user, I just put that into the where clause so it doesn't need to consider anyone else.

Now, one thing I noticed in what you have, and don't know if its intentional, or not. But since you are requerying the exact set of rows each time, but for the different <= date range, all your counts would be incremental of the prior. Ex: All rows prior to 1 day ago will be AT LEAST everything from 7 + 6 + 5 + 4 + 3 + 2 days ago. Similar for 2 days ago will be AT LEAST everything from 7 + 6 + 5 + 4 + 3 days ago.

select
      count(*) tpT,
      sum( case when date(p.timePosted) = @Day0 then 1 else 0 end ) as tdvT,
      sum( case when date(p.timePosted) <= @Day1 then 1 else 0 end ) as TP1,
      sum( case when date(p.timePosted) <= @Day2 then 1 else 0 end ) as TP2,
      sum( case when date(p.timePosted) <= @Day3 then 1 else 0 end ) as TP3,
      sum( case when date(p.timePosted) <= @Day4 then 1 else 0 end ) as TP4,
      sum( case when date(p.timePosted) <= @Day5 then 1 else 0 end ) as TP5,
      sum( case when date(p.timePosted) <= @Day6 then 1 else 0 end ) as TP6,
      sum( case when date(p.timePosted) <= @Day7 then 1 else 0 end ) as TP7
   from
      posts p,
      ( select @Day0 := date( now()),
               @Day1 := date_add( date(now()), interval -1 day ),
               @Day2 := date_add( date(now()), interval -2 day ),
               @Day3 := date_add( date(now()), interval -3 day ),
               @Day4 := date_add( date(now()), interval -4 day ),
               @Day5 := date_add( date(now()), interval -5 day ),
               @Day6 := date_add( date(now()), interval -6 day ),
               @Day7 := date_add( date(now()), interval -7 day ) ) SQLVars
   where 
      p.userID = 2

If what you really wanted was how many actual posts ex: from Today, Just one Day ago, Just TWO days ago, Just THREE days ago you would have to adjust your query to remove the <= to only "="

Ex: assume the following week of activity of actual posts.

Even before this Sunday,    Sun  Mon  Tue  Wed  Thu  Fri Sat
 200                         10   20   30   20   10   50  60


Using the <= approach your 
TP1 (representing Saturday 1 day ago) would be 400.  
TP2 (2 days ago -- Friday and prior) would be 340, 
TP3 (3 days ago -- Thursday and prior) would be 290, etc

If you expect the chart to show that (in this example), each day had their respective counts, just change from "<=" to "=".

Overall a slight difference from your query where it processes each field every time. This query process 1 record and accounts for all column result values. Just another option to a solution you have. I don't know performance impact on such a small table, but it might be more measurable if you have someone with 1000's of post activity.

You can simplify your sql and use this instead. It will give you all the counts of the posts posted in the decreasing order of the dates. So you will have all the past 7 days post. Now in your application login you just need to handle this data. This will simplify your sql and make it perform efficiently.

    select count(*)
    from posts
    where userId=2
    group by DATE(timePosted)
    order by DATE(timePosted) desc
    limit 7

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM