isync has asked for the wisdom of the Perl Monks concerning the following question:

More a question regarding database design...

What would be the best way to implement a stats counter, say "visits this week" and "visits all time" in SQL?

The "all time" counter is simple: having a database with an entry for each object to be counted, I would just increment the visits count field on each hit. But what about the "this week function".

Keeping track of recent visits could be done by using a timestamp on each visit count, but that would require piling up quite an amount of data in a seperate database for hit logging. Alternatively I could implement a database field for every, say day, so I could increase the "current day" field and for the "this week" function I would add all 7 fields. Like keeping track of a week in a rolling manner.

Or is there a simpler solution, more obvious, which I just didn't think of? How would you do it?
  • Comment on Best way to implement a stats counter ("this week" AND "all-time")

Replies are listed 'Best First'.
Re: Best way to implement a stats counter ("this week" AND "all-time")
by roboticus (Chancellor) on Sep 13, 2007 at 10:28 UTC
    isync:

    Have a counter table with a row for each day containing a hit count. I use the date in form YYYYMMDD as a key, and a simple int as the counter. For each hit, just find the current date and increment the bucket. Each night, add all days older than 7 days to bucket '00000000'.

    So for "visits this week" you'd use:

    select sum(counter) from hit_counts where hit_date > dateadd(d,-7,getdate())

    and for "visits all time":

    select sum(counter) from hit_counts

    Note: I've used Sybase/MS SQL Sever syntax here, may vary for other DB servers....

    ...roboticus

      ...and when you add to bucket "00000000" be sure to delete the (no longer needed) rows you're adding to it!

      ...roboticus

Re: Best way to implement a stats counter ("this week" AND "all-time")
by moritz (Cardinal) on Sep 13, 2007 at 10:48 UTC
Re: Best way to implement a stats counter ("this week" AND "all-time")
by isync (Hermit) on Sep 13, 2007 at 11:23 UTC
    Thanks everyone!

    UPDATE 2012-08-03
    Today, I had to implement this logic and it would be great to get some feedback, as I only got it working in a quite hackish way - especially I couldn't get the shorthand SQL part working so I had to resort to an if/else switch...
    ## SQL table schema: $sql = "create table if not exists views ( bucket integer, id integer null, views integer DEFAULT (0) );";
    sub api_get_views { my $id = shift; my $increment = shift; ## get views so far my $views = database->prepare("SELECT * FROM views WHERE id = ?; " +) or error database->errstr; $views->execute( $id ); my ($today_bucket) = split(/\s/, HTTP::Date::time2iso()); $today_bucket =~ s/-//g; my $cnt=0; my $bucket_exists; while( my $bucket = $views->fetchrow_hashref ){ $cnt += $bucket->{views}; $bucket_exists = 1 if $bucket->{bucket} == $today_bucket; } if($increment){ if($bucket_exists){ my $views = database->prepare("UPDATE views SET views = vi +ews + 1 WHERE bucket = ? AND id = ?; ") or error database->errstr; $views->execute( $today_bucket, $id ); }else{ my $views = database->prepare("INSERT INTO views (bucket,i +d,views) VALUES (?,?,?); ") or error database->errstr; $views->execute( $today_bucket, $id, 1 ); } $cnt++; } return $cnt; }