Reputation: 9007
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.
Upvotes: 0
Views: 4264
Reputation: 48139
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.
Upvotes: 3
Reputation: 2200
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.
Upvotes: 2